Android 自定義圓形頭像

埋頭丶苦幹發表於2017-09-20

Android 自定義圓形頭像

先上效果圖,如下:
這裡寫圖片描述

這是有邊框的圓形ImageView的效果圖,如果想沒有邊框或者改變邊框的顏色都可以通過程式碼進行設定,下面直接上程式碼:

package com.example.admin.viewtest;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.View;

/**
 * 此類實現了圓形ImageView的功能
 */
public class CircleImageView extends View {

    private Bitmap mBitmap;
    private int mLineColor;
    private int mLineWidth;
    private Paint mPaint = new Paint();
    private Context mContext;

    public CircleImageView(Context context) {
        super(context);
        init(context, null);
    }

    public CircleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        this.mContext = context;
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
        BitmapDrawable drawable = (BitmapDrawable) ta.getDrawable(R.styleable.CircleImageView_src);
        if (drawable != null)
            mBitmap = drawable.getBitmap();
        mLineColor = ta.getColor(R.styleable.CircleImageView_line_color, Color.WHITE);
        mLineWidth = (int) ta.getDimension(R.styleable.CircleImageView_line_width, 0);
        squareBitmap();
        ta.recycle();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mLineColor);
        mPaint.setStrokeWidth(mLineWidth);
    }

    /**
     * 將圖片處理為正方形圖片
     */
    private void squareBitmap() {
        if (mBitmap != null && mBitmap.getWidth() != mBitmap.getHeight()) {
            int wh = Math.min(mBitmap.getWidth(), mBitmap.getHeight());
            int num = Math.abs((mBitmap.getWidth() - mBitmap.getHeight()) / 2);
            if (mBitmap.getHeight() >= mBitmap.getWidth()) {
                mBitmap = Bitmap.createBitmap(mBitmap, 0, num, wh, wh);
            } else {
                mBitmap = Bitmap.createBitmap(mBitmap, num, 0, wh, wh);
            }
        }
    }

    @Override
    public void draw(Canvas canvas) {
        if (mBitmap != null) {
            int w = getWidth() - getPaddingLeft() - getPaddingRight();
            int h = getHeight() - getPaddingTop() - getPaddingBottom();
            int bw = mBitmap.getWidth();
            /**
             * 根據畫布大小縮放圖片
             */
            Matrix matrix = new Matrix();
            if (w >= h) {
                matrix.setScale(h / (float) bw, h / (float) bw);
            } else {
                matrix.setScale(w / (float) bw, w / (float) bw);
            }
            mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, bw, bw, matrix, true);
            int width = 0;
            int left;
            int top;
            /**
             * 確定圖片的位置,這裡考慮了設定的padding,如果不處理padding的值,那麼設定的padding將失效
             */
            if (mBitmap != null)
                width = mBitmap.getWidth();
            if (w >= h) {
                left = getPaddingLeft() + Math.abs((w - h) / 2);
                top = getPaddingTop();
            } else {
                left = getPaddingLeft();
                top = getPaddingTop() + Math.abs((w - h) / 2);
            }
            canvas.drawBitmap(createCircleImage(mBitmap, width), left, top, mPaint);
            mPaint.setColor(mLineColor);
            mPaint.setStrokeWidth(mLineWidth);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setAntiAlias(true);
            canvas.drawCircle((width / 2) + left, width / 2 + top, width / 2 - mLineWidth, mPaint);
        }
    }

    /**
     * 繪製圓形圖片
     *
     * @param source
     * @param min
     * @return
     */
    private Bitmap createCircleImage(Bitmap source, int min) {
        Paint p = new Paint();
        p.setAntiAlias(true);
        Bitmap target = Bitmap.createBitmap(min, min, Bitmap.Config.ARGB_8888);
        /**
         * 產生一個同樣大小的畫布
         */
        Canvas canvas = new Canvas(target);
        /**
         * 首先繪製圓形
         */
        canvas.drawCircle(min / 2, min / 2, min / 2 - mLineWidth, p);
        /**
         * 使用SRC_IN,參考上面的說明
         */
        p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        /**
         * 繪製圖片
         */
        canvas.drawBitmap(source, 0, 0, p);
        return target;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMeasureMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightMeasureSize = MeasureSpec.getSize(heightMeasureSpec);
        int pd = getPaddingBottom();
        int pt = getPaddingTop();
        int pl = getPaddingLeft();
        int pr = getPaddingRight();
        int width = 0;
        int height = 0;
        /**
         * 獲取設定的圖片的寬高
         */
        if (mBitmap != null) {
            width = mBitmap.getWidth();
            height = mBitmap.getHeight();
        }
        /**
         *當寬、高設定為wrap_content時,Mode為MeasureSpec.AT_MOST最大模式;
         *當寬、高設定為match_parent或者具體的值時,Mode為MeasureSpec.EXACTLY精確模式;
         *在測量View的寬、高時,需要對padding做處理,不然設定的padding將會失效
         */
        if (widthMeasureMode == MeasureSpec.AT_MOST && heightMeasureMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(width + pl + pr, height + pd + pt);
        } else if (widthMeasureMode == MeasureSpec.AT_MOST && heightMeasureMode == MeasureSpec.EXACTLY) {
            setMeasuredDimension(width + pl + pr, heightMeasureSize);
        } else if (widthMeasureMode == MeasureSpec.EXACTLY && heightMeasureMode == MeasureSpec.EXACTLY) {
            setMeasuredDimension(widthMeasureSize, heightMeasureSize);
        } else if (widthMeasureMode == MeasureSpec.EXACTLY && heightMeasureMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthMeasureSize, height + pd + pt);
        }
    }

    public void setImageBitmap(Bitmap bitmap) {
        recycleBitmap();
        this.mBitmap = bitmap;
        squareBitmap();
        invalidate();
    }

    public void setImageDrawable(Drawable drawable) {
        setImageBitmap(drawable == null ? null : drawableToBitmap(drawable));
    }

    public void setImageResource(int resId) {
        setImageDrawable(ContextCompat.getDrawable(mContext, resId));
    }

    public void setLineColorResource(int resId) {
        setLineColor(ContextCompat.getColor(mContext, resId));
    }

    public void setLineColor(int color) {
        mLineColor = color;
        invalidate();
    }

    /**
     * 設定線的寬度
     *
     * @param width 這個寬度的單位是px
     */
    public void setLineWidth(int width) {
        mLineWidth = width;
        invalidate();
    }

    private Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        } else if (drawable instanceof NinePatchDrawable) {
            // 取 drawable 的長寬
            int w = drawable.getIntrinsicWidth();
            int h = drawable.getIntrinsicHeight();

            // 取 drawable 的顏色格式
            Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                    : Bitmap.Config.RGB_565;
            // 建立對應 bitmap
            Bitmap bitmap = Bitmap.createBitmap(w, h, config);

            // 建立對應 bitmap 的畫布
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, w, h);
            // 把 drawable 內容畫到畫布中
            drawable.draw(canvas);

            return bitmap;
        } else {
            return null;
        }
    }


    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        recycleBitmap();
    }

    /**
     * 釋放圖片資源
     */
    private void recycleBitmap() {
        if (mBitmap != null) {
            mBitmap.recycle();
            mBitmap = null;
        }
    }
}

上面就是自定義View的全部程式碼了,程式碼的關鍵位置都寫了註釋,同時也很簡單,相信大家一看就懂了,所以不多做解釋。本自定義圓形ImageView為了方便在佈局中設定圖片、邊框顏色和邊框寬度,在attrs.xml 檔案中新增了以下屬性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleImageView">
        <attr name="src" format="reference"></attr>
        <attr name="line_color" format="color"></attr>
        <attr name="line_width" format="dimension"></attr>
    </declare-styleable>
</resources>

同時在初始化控制元件的時候獲取了設定的屬性:

       TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
        BitmapDrawable drawable = (BitmapDrawable) ta.getDrawable(R.styleable.CircleImageView_src);
        if (drawable != null)
            mBitmap = drawable.getBitmap();
        mLineColor = ta.getColor(R.styleable.CircleImageView_line_color, Color.WHITE);
        mLineWidth = (int) ta.getDimension(R.styleable.CircleImageView_line_width, 0);
        ta.recycle();

這裡需要注意在使用完TypedArray 之後,需要及時的呼叫ta.recycle()釋放資源。

以上就是自定義圓形ImageView的所有程式碼和說明了,大家有不懂的歡迎留言。

相關文章