Android自定義圓形圓角圖片示例

本文參考自大神張鴻洋的文章,同時修復了一個我自己發現的問題(因爲不知道算不算Bug):當顯示圓形圖片時,若在xml中設置的圖片的寬高屬性爲wrap_content,而所用的圖片寬高比不爲1。當width>height時,圖片只顯示從左邊開始長度爲height值的部分;當height>width時,圖片只顯示從上邊開始高度爲width值的部分;所以我做了一點處理,使其達到類似於 android:scaleType=”center”的功能。

圖片顯示效果

demo地址爲:Android自定義圓形圓角圖片示例

這裏只貼自定義View代碼如下:

package com.android.circularpicture;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ImageView;

/**
 * 自定義圓形ImageView
 * 作者: k.k on 2017/8/29.
 * 郵箱:[email protected]
 */

public class CircularImageView extends ImageView {

    //圖片類型(circle/round)
    private int type;

    private Bitmap mImg;//圖片

    private int mWidth;//控件的寬度

    private int mHeight;//控件的高度

    private int mRadius;//圓角的半徑

    private Bitmap circleView;//加載的圓形View

    private Bitmap roundView;//加載的圓角View

    /*這個構造方法必須要*/
    public CircularImageView(Context context) {
        this(context, null);
    }

    /*這個構造方法必須要*/
    public CircularImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    /*真正的執行在這裏*/
    public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray typedarray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularImageView, defStyle, 0);
        int counts = typedarray.getIndexCount();
        for (int i = 0; i < counts; i++) {
            int attr = typedarray.getIndex(i);
            switch (attr) {
                case R.styleable.CircularImageView_src:
                    mImg = BitmapFactory.decodeResource(getResources(), typedarray.getResourceId(attr, 0));
                    break;
                case R.styleable.CircularImageView_borderRadius:
                    int defValue = (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_DIP, 10f, getResources().getDisplayMetrics()
                    );//applyDimension()是一個將各種單位的值轉換爲像素的方法,第一個參數是指第二個參數值得單位,並將該單位的值轉換爲px,在這裏TypedValue.COMPLEX_UNIT_DIP表示dp,即10f表示10dp
                    mRadius = typedarray.getDimensionPixelSize(attr, defValue);
                    break;
                case R.styleable.CircularImageView_type:
                    type = typedarray.getInt(attr, 0);
                    break;
            }
        }
        typedarray.recycle();//記得回收掉
    }


    //重寫onMeasure方法,計算控件的寬度和高度
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

         /*設置寬度*/
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        //類型:match parent(繼承父佈局的值),accurate(精確的值)
        if (specMode == MeasureSpec.EXACTLY) {
            mWidth = specSize;//此時,控件的寬度即爲specSize
        } else {
            //由控件自身決定寬度
            int demandWidthImg = getPaddingLeft() + getPaddingRight() + mImg.getWidth();

            //類型:wrap_parent
            if (specMode == MeasureSpec.AT_MOST) {
                mWidth = Math.min(demandWidthImg, specSize);//返回較小的那個數值
            }
            //這種情況很少使用到
            else {
                mWidth = specSize;
            }
        }

        /*設置高度*/
        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            mHeight = specSize;
        } else {
            int demandHeightImg = getPaddingTop() + getPaddingBottom() + mImg.getHeight();

            if (specMode == MeasureSpec.AT_MOST) {
                mHeight = Math.min(demandHeightImg, specSize);
            } else {
                mHeight = specSize;
            }
        }

        setMeasuredDimension(mWidth, mHeight);//設置View寬高的值(實際叫測量值)
    }


    /*重寫onDraw方法,繪製對應的圖片*/
    @Override
    protected void onDraw(Canvas canvas) {
        float horizontalOffset = mWidth > mHeight ? (mWidth - mHeight) / 2 : (mHeight - mWidth) / 2;//需要調整的偏移量
        switch (type) {
            //繪製圓形圖片
            case Constant.CIRCLE_TYPE:
                int diameter = Math.min(mWidth, mHeight);//因爲是繪製圓形圖片,如果mWidth,mHeight不一致,取小的值作爲圓圖的直徑
                mImg = Bitmap.createScaledBitmap(mImg, diameter, diameter, false);//以w,h爲目標對bitmap進行縮放,filter表示是否要對位圖進行過濾(濾波)處理這個方法的效果就類似於ImageView.ScaleType.FIT_XY
                if (mWidth > mHeight) {
                    canvas.drawBitmap(createCircleView(mImg, diameter), horizontalOffset, 0, null);
                } else {
                    canvas.drawBitmap(createCircleView(mImg, diameter), 0, horizontalOffset, null);
                }
                circleView = null;
                break;
            //繪製帶圓角的圖片
            case Constant.ROUND_TYPE:
                canvas.drawBitmap(createRoundView(mImg), 0, 0, null);
                roundView = null;
                break;
        }
    }

    /*繪製圓形View*/
    private Bitmap createCircleView(Bitmap bitmap, int diameter) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);//設置抗鋸齒方法
        circleView = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(circleView);//產生一個同樣大小的畫布

        canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);//繪製圓形

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

        canvas.drawBitmap(bitmap, 0, 0, paint);

        return circleView;
    }

    /*繪製圓角View*/
    private Bitmap createRoundView(Bitmap bitmap) {
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        roundView = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(roundView);

        RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());

        canvas.drawRoundRect(rect, mRadius, mRadius, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

        canvas.drawBitmap(bitmap, 0, 0, paint);

        return roundView;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章