1. 程式人生 > >Android中圓形圖的幾種實現方式

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中實現圓形圖片的幾種方案了,各有優缺點,還有更好的解決方案,可以留言討論~