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


發佈了29 篇原創文章 · 獲贊 201 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章