Android中圓形圖的幾種實現方式
在Android開發中,圓形圖片是很常見的,例如淘寶的寶貝,QQ的聯絡人頭像等都是圓形的圖片,
但是Android原生的ImageView又不能顯示圓形的圖片,這就需要我們自己去實現一個圓形圖了
一、自定義View實現圓形圖
我們可以去改造Android系統自帶的ImageView來讓它顯示圓形圖片,具體思路是利用畫筆的層疊屬性,在圖片的底部繪製一個圓形,然後顯示上下兩層的交集部分,就可以做出一個圓形的ImageView了1.1繼承ImageView
import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.ImageView; /** * Created by ChenFengYao on 16/3/15. */ public class RoundImageView extends ImageView { private Paint paint; public RoundImageView(Context context) { super(context); paint = new Paint();//初始化畫筆物件 } public RoundImageView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint();//初始化畫筆物件 } public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); paint = new Paint();//初始化畫筆物件 } }
繼承ImageView複寫其中的構造方法,並在構造方法裡對畫筆物件進行初始化
1.2複寫onDraw方法
@Override protected void onDraw(Canvas canvas) { Drawable drawable = getDrawable(); if (null != drawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); //核心程式碼 Bitmap b = getCircleBitmap(bitmap); paint.reset(); canvas.drawBitmap(b,0,0,paint); } else { super.onDraw(canvas); } }
onDraw方法即圖片繪製的時候系統所呼叫的方法,在該方法內部首先去判斷是否設定了圖片的src,如果能拿到改View設定的圖片,則將它轉換成圓形圖片,如果沒有設定的話,則不做任何操作,直接呼叫父類的onDraw方法
1.2getCircleBitmap
private Bitmap getCircleBitmap(Bitmap bitmap){ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); Rect rect = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()); paint.setAntiAlias(true); canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getHeight() / 2, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint);//將圖片畫出來 return output; }
這是獲取圓形圖的方法,目的是將我們自定義的View中的圖片,轉化成圓形的Bitmap,這裡需要Canvas物件,首先畫一個圓形的底層,再在其上放上我們的圖片,通過設定畫筆的paint的Xfermode屬性,該屬性是用來設定前後圖層的顯示關係的,這是設定Mode.SRC_IN,的意思是輸出的範圍是底層圖形的範圍,而顯示的內容是上層的內容
1.2.1paint.setXfermode()
Xfermode有大神稱之為過渡模式,這種翻譯比較貼切但恐怕不易理解,大家也可以直接稱之為影象混合模式,因為所謂的“過渡”其實就是影象混合的一種,簡單來說,可以理解成圖片的疊加方式.我們可以看一下圖片的16中疊加方式
PorterDuff.Mode.CLEAR |
所繪製不會提交到畫布上。 |
PorterDuff.Mode.SRC |
顯示上層繪製圖片 |
PorterDuff.Mode.DST |
顯示下層繪製圖片 |
PorterDuff.Mode.SRC_OVER |
正常繪製顯示,上下層繪製疊蓋 |
PorterDuff.Mode.DST_OVER |
上下層都顯示。下層居上顯示 |
PorterDuff.Mode.SRC_IN |
取兩層繪製交集。顯示上層 |
PorterDuff.Mode.DST_IN |
取兩層繪製交集。顯示下層 |
PorterDuff.Mode.SRC_OUT |
取上層繪製非交集部分 |
PorterDuff.Mode.DST_OUT |
取下層繪製非交集部分 |
PorterDuff.Mode.SRC_ATOP |
取下層非交集部分與上層交集部分 |
PorterDuff.Mode.DST_ATOP |
取上層非交集部分與下層交集部分 |
PorterDuff.Mode.XOR |
取兩層繪製非交集。兩層繪製非交集 |
PorterDuff.Mode.DARKEN |
上下層都顯示。變暗 |
PorterDuff.Mode.LIGHTEN |
上下層都顯示。變亮 |
PorterDuff.Mode.MULTIPLY |
取兩層繪製交集 |
PorterDuff.Mode.SCREEN |
上下層都顯示 |
1.2.2Canvas
Canvas類可以繪製各種的圖形,在繪製的時候會將繪製好的內容儲存在Canvas的內部,可以將所繪製的內容輸出為一張Bitmap,這張Bitmap即在new Canvas的時候通過構造方法傳進去.而在onDraw方法中傳入的Canvas則會在繪製完畢後,直接將內部的內容輸出到螢幕上的.1.3測試
寫好了之後,我們來測試一下<com.lanou.chenfengyao.temproundimageview.RoundImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/test_img"/>
看 圖片已經被處理成圓形的了
1.4 新增功能
我們希望可以用我們自己的RoundImageView來實現即可以顯示正常的ImageView也可以顯示圓形圖片的功能,最好可以能用Java程式碼動態控制.於是我們新增一個自定義屬性 首先在values下新建attrs.xml 程式碼如下<resources>
<declare-styleable name="RoundImageView">
<attr name="is_round" format="boolean" />
</declare-styleable>
</resources>
可以看到 我們定義了一條自定義屬性:is_round 它是一個boolean值的屬性,當屬性是false的時候 我們就顯示一個正常的圖片,當屬性是true的時候,則顯示一個圓形圖片
在RoundImageView中新增一個變數
private boolean isRound;
然後在構造方法裡提取這條屬性,如果沒有提取到,預設值設定為true,讓它預設就可以顯示圓角圖片
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();//初始化畫筆物件
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);
isRound = typedArray.getBoolean(R.styleable.RoundImageView_is_round, true);
}
為了增加實用性 我們改造一下剩下的構造方法
public RoundImageView(Context context) {
this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
接下來在onDraw方法裡進行一次判斷,如果需要顯示圓形圖,才顯示圓形圖
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (null != drawable && isRound) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
//核心程式碼
Bitmap b = getCircleBitmap(bitmap);
paint.reset();
canvas.drawBitmap(b, 0, 0, paint);
b.recycle();
} else {
super.onDraw(canvas);
}
}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">測試一下,我們將我們的元件的is_round屬性改成false看看效果</span>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.lanou.chenfengyao.temproundimageview.MainActivity">
<com.lanou.chenfengyao.temproundimageview.RoundImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/test_img"
app:is_round="false"/>
</RelativeLayout>
注意,我們自定義的屬性,名稱空間不要忘記了
來看看效果
可以看到,現在的RoundImageView就和正常的ImageView的效果是一樣的啦. 現在我們再寫一個方法能讓RoundImgeView動態的改變圓形或是正常的,在RoundImageView裡新增方法
public void setIsRound(boolean isRound) {
this.isRound = isRound;
invalidate();
}
現在我們在Activity裡放上一個按鈕,點選它切換顯示模式,來測試一下
可以看到 我們的圖片可以通過Java程式碼來動態的切換正常模式和圓形圖片啦
二、使用Fresco顯示圓形圖片
Fresco是FaceBook推出的專門用來載入圖片的類庫,它可以載入網路圖片,並且有豐富的效果,並且最重要的是,它具有中文文件! 文件地址http://www.fresco-cn.org/ 根據文件我們首先在gradle里加上compile 'com.facebook.fresco:fresco:0.9.0+'
另外Fresco在使用的時候需要對其進行初始化,可以在需要的Activity裡的onCreate方法裡新增
Fresco.initialize(this);
建議將這行程式碼新增到Application裡進行全域性的初始化
在使用起來就變得很簡單了,只需要在xml裡新增
<com.facebook.drawee.view.SimpleDraweeView
fresco:roundAsCircle = "true"
fresco:actualImageScaleType="centerInside"
fresco:roundingBorderColor="@color/colorAccent"
fresco:roundingBorderWidth="1dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
fresco:backgroundImage="@mipmap/test_img"/>
並且不要忘記名稱空間
xmlns:fresco="http://schemas.android.com/apk/res-auto"
看一下效果
可以看到出現了圓形圖片,並且還有1dp的紅色邊框
2.1一些坑
Fresco在這種使用方式的時候有一些坑是值得注意的,首先根據官方的說法,會在後續的版本中不再繼承自ImageView,所以不建議使用ImageView的一些屬性和方法,例如src等. 第二,該空間無法使用wrap_content的方式來指定寬高,這點略坑...三、利用sharp做圓形圖
我們也可以使用ImageView和sharp來完成一個圓形圖片的效果,首先在drawable裡新建circle.xml<shape
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="1"
android:useLevel="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent" />
<stroke
android:width="68dp"
android:color="#FFFFFFFF" />
</shape>
讓shape是ring即環形,並且設定填充顏色為白色,線寬需要根據圖片自己調整
然後再drable裡新建round_layers.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/test_img" />
<item android:drawable="@drawable/circle" />
</layer-list>
layer-list的意思是讓ImageView中顯示的圖片產生一個疊加的效果,越靠下寫的,在顯示的時候就越靠上層
最後讓ImageView使用該xml
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/round_layers"/>
看一下效果吧
實際上,該方法的原理就是在正常的ImageView上再疊加了一個白色的環形圖形,這樣顯示的效果就是圓形圖片了.但是缺點也是顯而易見的,即需要手動的調節環形的粗細,比較難控制 以上就是Android中實現圓形圖片的幾種方案了,各有優缺點,還有更好的解決方案,可以留言討論~