轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自:【張鴻洋的博客】
1、概述
記得初學那會寫過一篇博客Android 完美實現圖片圓角和圓形(對實現進行分析),主要是個自定View加上使用Xfermode實現的。其實實現圓角圖片的方法應該很多,常見的就是利用Xfermode,Shader。本篇博客會直接繼承直接繼承ImageView,使用BitmapShader實現圓角的繪制,大家如果耐著性子看完,我估計什么形狀都能繪制出來。
2、效果圖
這是圓角的一個演示圖~~這個沒什么說的,直接設置的圓角的大小就行;
?
?
這是圓形的顯示圖,這里需要注意下,因為設置的圖片可能是長方形,例如上圖:有兩個長方形,一個寬比較大,一個高比較大;
那么我們希望顯示成圓形,我們可能就要對其進行放大或者縮小(因為圖片的寬可能不滿足設置的邊長,而高超出,此時我們就需要放大其寬度)。
?
?
這個一張圖,中間是正常尺寸;上下分別為特大特小,主要可以當尺寸大于或者小于設置尺寸,我們需要對其放大或者縮小;
圓角時如果圖片與view的寬高不一致,也需要進行放大縮小,這里就不截圖了,代碼里面看吧。
3、淺談BitmapShader
BitmapShader是Shader的子類,可以通過Paint.setShader(Shader shader)進行設置、
這里我們只關注BitmapShader,構造方法:
mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
參數1:bitmap
參數2,參數3:TileMode;
TileMode的取值有三種:
CLAMP 拉伸
REPEAT 重復
MIRROR 鏡像
如果大家給電腦屏幕設置屏保的時候,如果圖片太小,可以選擇重復、拉伸、鏡像;
重復:就是橫向、縱向不斷重復這個bitmap
鏡像:橫向不斷翻轉重復,縱向不斷翻轉重復;
拉伸:這個和電腦屏保的模式應該有些不同,這個拉伸的是圖片最后的那一個像素;橫向的最后一個橫行像素,不斷的重復,縱項的那一列像素,不斷的重復;
現在大概明白了,BitmapShader通過設置給mPaint,然后用這個mPaint繪圖時,就會根據你設置的TileMode,對繪制區域進行著色。
這里需要注意一點:就是BitmapShader是從你的畫布的左上角開始繪制的,不在view的右下角繪制個正方形,它不會在你正方形的左上角開始。
好了,到此,我相信大家對BitmapShader有了一定的了解了;當然了,如果你希望對Shader充分的了解,請參考愛歌的神作:?自定義控件其實很簡單1/3?。
對于我們的圓角,以及圓形,我們設置的模式都是CLAMP ,但是你會不會會有一個疑問:
view的寬或者高大于我們的bitmap寬或者高豈不是會拉伸?
嗯,我們會為BitmapShader設置一個matrix,去適當的放大或者縮小圖片,不會讓“?view的寬或者高大于我們的bitmap寬或者高 ”此條件成立的。
到此我們的原理基本介紹完畢了,拿到drawable轉化為bitmap,然后直接初始化BitmapShader,畫筆設置Shader,最后在onDraw里面進行畫圓就行了。
4、BitmapShader實戰
首先就來看看利用BitmapShader實現的圓形或者圓角。
我們這里直接繼承ImageView,這樣大家設置圖片的代碼會比較熟悉;但是我們需要支持兩種模式,那么就需要自定義屬性了:
1、自定義屬性
values/attr.xml
[html] view plaincopy
<?xml?version="1.0"?encoding="utf-8"?>??<resources>????????<attr?name="borderRadius"?format="dimension"?/>??????<attr?name="type">??????????<enum?name="circle"?value="0"?/>??????????<enum?name="round"?value="1"?/>??????</attr>????????????<declare-styleable?name="RoundImageView">??????????<attr?name="borderRadius"?/>??????????<attr?name="type"?/>??????</declare-styleable>????</resources>?? 我們定義了一個枚舉和一個圓角的大小borderRadius。
2、獲取自定義屬性
[java] view plaincopy
public?class?RoundImageView?extends?ImageView??{????????????private?int?type;??????private?static?final?int?TYPE_CIRCLE?=?0;??????private?static?final?int?TYPE_ROUND?=?1;????????????private?static?final?int?BODER_RADIUS_DEFAULT?=?10;??????????private?int?mBorderRadius;????????????private?Paint?mBitmapPaint;??????????private?int?mRadius;??????????private?Matrix?mMatrix;??????????private?BitmapShader?mBitmapShader;??????????private?int?mWidth;??????private?RectF?mRoundRect;????????public?RoundImageView(Context?context,?AttributeSet?attrs)??????{??????????super(context,?attrs);??????????mMatrix?=?new?Matrix();??????????mBitmapPaint?=?new?Paint();??????????mBitmapPaint.setAntiAlias(true);????????????TypedArray?a?=?context.obtainStyledAttributes(attrs,??????????????????R.styleable.RoundImageView);????????????mBorderRadius?=?a.getDimensionPixelSize(??????????????????R.styleable.RoundImageView_borderRadius,?(int)?TypedValue??????????????????????????.applyDimension(TypedValue.COMPLEX_UNIT_DIP,??????????????????????????????????BODER_RADIUS_DEFAULT,?getResources()??????????????????????????????????????????.getDisplayMetrics()));????????type?=?a.getInt(R.styleable.RoundImageView_type,?TYPE_CIRCLE);??????????a.recycle();??????}?? ?
可以看到我們的一些成員變量,基本都加了注釋;然后在構造方法中獲取了我們的自定義屬性,以及部分變量的初始化。
?
3、onMeasure
[java] view plaincopy
@Override??????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)??????{??????????Log.e("TAG",?"onMeasure");??????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);????????????????????if?(type?==?TYPE_CIRCLE)??????????{??????????????mWidth?=?Math.min(getMeasuredWidth(),?getMeasuredHeight());??????????????mRadius?=?mWidth?/?2;??????????????setMeasuredDimension(mWidth,?mWidth);??????????}????????}??
我們復寫了onMeasure方法,主要用于當設置類型為圓形時,我們強制讓view的寬和高一致。
接下來只剩下設置BitmapShader和繪制了
?
4、設置BitmapShader
[java] view plaincopy
????private?void?setUpShader()??????{??????????Drawable?drawable?=?getDrawable();??????????if?(drawable?==?null)??????????{??????????????return;??????????}????????????Bitmap?bmp?=?drawableToBitamp(drawable);??????????????????mBitmapShader?=?new?BitmapShader(bmp,?TileMode.CLAMP,?TileMode.CLAMP);??????????float?scale?=?1.0f;??????????if?(type?==?TYPE_CIRCLE)??????????{??????????????????????????int?bSize?=?Math.min(bmp.getWidth(),?bmp.getHeight());??????????????scale?=?mWidth?*?1.0f?/?bSize;????????????}?else?if?(type?==?TYPE_ROUND)??????????{??????????????????????????scale?=?Math.max(getWidth()?*?1.0f?/?bmp.getWidth(),?getHeight()??????????????????????*?1.0f?/?bmp.getHeight());??????????}??????????????????mMatrix.setScale(scale,?scale);??????????????????mBitmapShader.setLocalMatrix(mMatrix);??????????????????mBitmapPaint.setShader(mBitmapShader);??????}?? 在setUpShader中,首先對drawable轉化為我們的bitmap;
然后初始化mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP);
接下來,根據類型以及bitmap和view的寬高,計算scale;
關于scale的計算:
圓形時:取bitmap的寬或者高的小值作為基準,如果采用大值,縮放后肯定不能填滿我們的圓形區域。然后,view的mWidth/bSize ; 得到的就是scale。
圓角時:因為設計到寬/高比例,我們分別getWidth() * 1.0f / bmp.getWidth() 和?getHeight()?* 1.0f / bmp.getHeight()?;最終取大值,因為我們要讓最終縮放完成的圖片一定要大于我們的view的區域,有點類似centerCrop;
比如:view的寬高為10*20;圖片的寬高為5*100 ; 最終我們應該按照寬的比例放大,而不是按照高的比例縮小;因為我們需要讓縮放后的圖片,自定大于我們的view寬高,并保證原圖比例。
有了scale,就可以設置給我們的matrix;
然后使用mBitmapShader.setLocalMatrix(mMatrix);
最后將bitmapShader設置給paint。
關于drawable轉bitmap的代碼:
[java] view plaincopy
????private?Bitmap?drawableToBitamp(Drawable?drawable)??????{??????????if?(drawable?instanceof?BitmapDrawable)??????????{??????????????BitmapDrawable?bd?=?(BitmapDrawable)?drawable;??????????????return?bd.getBitmap();??????????}??????????int?w?=?drawable.getIntrinsicWidth();??????????int?h?=?drawable.getIntrinsicHeight();??????????Bitmap?bitmap?=?Bitmap.createBitmap(w,?h,?Bitmap.Config.ARGB_8888);??????????Canvas?canvas?=?new?Canvas(bitmap);??????????drawable.setBounds(0,?0,?w,?h);??????????drawable.draw(canvas);??????????return?bitmap;??????}?? 最后我們會在onDraw里面調用setUpShader(),然后進行繪制。
5、繪制
到此,就剩下最后一步繪制了,因為我們的范圍,以及縮放都完成了,所以真的只剩下繪制了。
[java] view plaincopy
@Override??????protected?void?onDraw(Canvas?canvas)??????{??????????if?(getDrawable()?==?null)??????????{??????????????return;??????????}??????????setUpShader();????????????if?(type?==?TYPE_ROUND)??????????{??????????????canvas.drawRoundRect(mRoundRect,?mBorderRadius,?mBorderRadius,??????????????????????mBitmapPaint);??????????}?else??????????{??????????????canvas.drawCircle(mRadius,?mRadius,?mRadius,?mBitmapPaint);??????????????????????}??????}????????????@Override??????protected?void?onSizeChanged(int?w,?int?h,?int?oldw,?int?oldh)??????{??????????super.onSizeChanged(w,?h,?oldw,?oldh);??????????????????if?(type?==?TYPE_ROUND)??????????????mRoundRect?=?new?RectF(0,?0,?getWidth(),?getHeight());??????}??
繪制就很簡單了,畫個圓,圓角矩形什么的。圓角矩形的限定范圍mRoundRect在onSizeChanged里面進行了初始化。
5、狀態的存儲與恢復
當然了,如果內存不足,而恰好我們的Activity置于后臺,不幸被重啟,或者用戶旋轉屏幕造成Activity重啟,我們的View應該也能盡可能的去保存自己的屬性。 狀態保存什么用處呢?比如,現在一個的圓角大小是10dp,用戶點擊后變成50dp;當用戶旋轉以后,或者長時間置于后臺以后,返回我們的Activity應該還是50dp; 我們簡單的存儲一下,當前的type以及mBorderRadius
[java] view plaincopy
private?static?final?String?STATE_INSTANCE?=?"state_instance";??????private?static?final?String?STATE_TYPE?=?"state_type";??????private?static?final?String?STATE_BORDER_RADIUS?=?"state_border_radius";????????@Override??????protected?Parcelable?onSaveInstanceState()??????{??????????Bundle?bundle?=?new?Bundle();??????????bundle.putParcelable(STATE_INSTANCE,?super.onSaveInstanceState());??????????bundle.putInt(STATE_TYPE,?type);??????????bundle.putInt(STATE_BORDER_RADIUS,?mBorderRadius);??????????return?bundle;??????}????????@Override??????protected?void?onRestoreInstanceState(Parcelable?state)??????{??????????if?(state?instanceof?Bundle)??????????{??????????????Bundle?bundle?=?(Bundle)?state;??????????????super.onRestoreInstanceState(((Bundle)?state)??????????????????????.getParcelable(STATE_INSTANCE));??????????????this.type?=?bundle.getInt(STATE_TYPE);??????????????this.mBorderRadius?=?bundle.getInt(STATE_BORDER_RADIUS);??????????}?else??????????{??????????????super.onRestoreInstanceState(state);??????????}????????}?? 代碼比較簡單。我們文章中的demo中,第一個,第四個是可以點擊的,點擊后會發生變化,你可以點擊后,然后旋轉屏幕進行測試。 同時我們也對外公布了兩個方法,用于動態修改圓角大小和type
[java] view plaincopy
public?void?setBorderRadius(int?borderRadius)??????{??????????int?pxVal?=?dp2px(borderRadius);??????????if?(this.mBorderRadius?!=?pxVal)??????????{??????????????this.mBorderRadius?=?pxVal;??????????????invalidate();??????????}??????}????????public?void?setType(int?type)??????{??????????if?(this.type?!=?type)??????????{??????????????this.type?=?type;??????????????if?(this.type?!=?TYPE_ROUND?&&?this.type?!=?TYPE_CIRCLE)??????????????{??????????????????this.type?=?TYPE_CIRCLE;??????????????}??????????????requestLayout();??????????}????????}????????public?int?dp2px(int?dpVal)??????{??????????return?(int)?TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,??????????????????dpVal,?getResources().getDisplayMetrics());??????}?? 最后貼一下我們的布局文件和MainActivity。
6、調用
布局文件:
[html] view plaincopy
<ScrollView?xmlns:android="http://schemas.android.com/apk/res/android"??????xmlns:tools="http://schemas.android.com/tools"??????xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.variousshapeimageview"??????android:layout_width="match_parent"??????android:layout_height="wrap_content"?>????????<LinearLayout??????????android:layout_width="match_parent"??????????android:layout_height="match_parent"??????????android:orientation="vertical"?>????????????<com.zhy.view.RoundImageView??????????????android:id="@+id/id_qiqiu"??????????????android:layout_width="wrap_content"??????????????android:layout_height="wrap_content"??????????????android:layout_margin="10dp"??????????????android:src="@drawable/qiqiu"?>??????????</com.zhy.view.RoundImageView>????????????<com.zhy.view.RoundImageView??????????????android:layout_width="200dp"??????????????android:layout_height="200dp"??????????????android:layout_margin="10dp"??????????????android:src="@drawable/aa"?>??????????</com.zhy.view.RoundImageView>????????????<com.zhy.view.RoundImageView??????????????android:layout_width="wrap_content"??????????????android:layout_height="wrap_content"??????????????android:layout_margin="10dp"??????????????android:src="@drawable/icon"?>??????????</com.zhy.view.RoundImageView>????????????<com.zhy.view.RoundImageView??????????????android:id="@+id/id_meinv"??????????????android:layout_width="wrap_content"??????????????android:layout_height="wrap_content"??????????????android:layout_margin="10dp"??????????????android:src="@drawable/aa"??????????????zhy:borderRadius="20dp"??????????????zhy:type="round"?>??????????</com.zhy.view.RoundImageView>????????????<com.zhy.view.RoundImageView??????????????android:layout_width="wrap_content"??????????????android:layout_height="wrap_content"??????????????android:layout_margin="10dp"??????????????android:src="@drawable/icon"??????????????zhy:borderRadius="40dp"??????????????zhy:type="round"?>??????????</com.zhy.view.RoundImageView>????????????<com.zhy.view.RoundImageView??????????????android:layout_width="wrap_content"??????????????android:layout_height="wrap_content"??????????????android:layout_margin="10dp"??????????????android:src="@drawable/qiqiu"??????????????zhy:borderRadius="60dp"??????????????zhy:type="round"?>??????????</com.zhy.view.RoundImageView>??????</LinearLayout>????</ScrollView>?? 沒撒,ScrollView里面一個線性布局,里面一堆RoundImageView。
MainActivity
[java] view plaincopy
package?com.zhy.variousshapeimageview;????import?android.app.Activity;??import?android.os.Bundle;??import?android.view.View;??import?android.view.View.OnClickListener;????import?com.zhy.view.RoundImageView;????public?class?MainActivity?extends?Activity??{??????private?RoundImageView?mQiQiu;??????private?RoundImageView?mMeiNv?;?????????@Override??????protected?void?onCreate(Bundle?savedInstanceState)??????{??????????super.onCreate(savedInstanceState);??????????setContentView(R.layout.activity_main);????????????????????mQiQiu?=?(RoundImageView)?findViewById(R.id.id_qiqiu);??????????mMeiNv?=?(RoundImageView)?findViewById(R.id.id_meinv);????????????????????mQiQiu.setOnClickListener(new?OnClickListener()??????????{??????????????@Override??????????????public?void?onClick(View?v)??????????????{??????????????????mQiQiu.setType(RoundImageView.TYPE_ROUND);??????????????}??????????});????????????????????mMeiNv.setOnClickListener(new?OnClickListener()??????????{????????????????????????????@Override??????????????public?void?onClick(View?v)??????????????{??????????????????mMeiNv.setBorderRadius(90);??????????????}??????????});??????}????}?? 好了,到此本篇博客就結束了。大家可以嘗試繪制個五邊形或者神馬的形狀;或者加個邊框神馬的,相信自己修改應該沒問題~~代碼可能會存在bug和不足之處,歡迎您的指出,共同進步。
?
最后的效果圖:
?
?
源碼點擊下載
轉載于:https://www.cnblogs.com/Free-Thinker/p/6721468.html
總結
以上是生活随笔為你收集整理的Android BitmapShader 实战 实现圆形、圆角图片的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。