Android 自定义圆形图片 CircleImageView

1.效果预览

1.1.布局中写自定义圆形图片的路径即可

  

 

1.2.然后看一看图片效果

  

 

1.3.原图是这样的 @mipmap/ic_launcher

  


2.使用过程

2.1.CircleImageView源代码 

public class CircleImageView extends AppCompatImageView {

    private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    private static final int COLORDRAWABLE_DIMENSION = 1;

    private static final int DEFAULT_BORDER_WIDTH = 0;
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK;

    private final RectF mDrawableRect = new RectF();
    private final RectF mBorderRect = new RectF();

    private final Matrix mShaderMatrix = new Matrix();
    private final Paint mBitmapPaint = new Paint();
    private final Paint mBorderPaint = new Paint();

    private int mBorderColor = DEFAULT_BORDER_COLOR;
    private int mBorderWidth = DEFAULT_BORDER_WIDTH;

    private Bitmap mBitmap;
    private BitmapShader mBitmapShader;
    private int mBitmapWidth;
    private int mBitmapHeight;

    private float mDrawableRadius;
    private float mBorderRadius;

    private boolean mReady;
    private boolean mSetupPending;

    public CircleImageView(Context context) {
        super(context);
    }

    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        super.setScaleType(SCALE_TYPE);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

        mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
        mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);

        a.recycle();

        mReady = true;

        if (mSetupPending) {
            setup();
            mSetupPending = false;
        }
    }

    @Override
    public ScaleType getScaleType() {
        return SCALE_TYPE;
    }

    @Override
    public void setScaleType(ScaleType scaleType) {
        if (scaleType != SCALE_TYPE) {
            throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (getDrawable() == null) {
            return;
        }

        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        setup();
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public void setBorderColor(int borderColor) {
        if (borderColor == mBorderColor) {
            return;
        }

        mBorderColor = borderColor;
        mBorderPaint.setColor(mBorderColor);
        invalidate();
    }

    public int getBorderWidth() {
        return mBorderWidth;
    }

    public void setBorderWidth(int borderWidth) {
        if (borderWidth == mBorderWidth) {
            return;
        }

        mBorderWidth = borderWidth;
        setup();
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        mBitmap = bm;
        setup();
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        mBitmap = getBitmapFromDrawable(drawable);
        setup();
    }

    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        mBitmap = getBitmapFromDrawable(getDrawable());
        setup();
    }

    private Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        try {
            Bitmap bitmap;

            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
            }

            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (OutOfMemoryError e) {
            return null;
        }
    }

    private void setup() {
        if (!mReady) {
            mSetupPending = true;
            return;
        }

        if (mBitmap == null) {
            return;
        }

        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        mBitmapPaint.setAntiAlias(true);
        mBitmapPaint.setShader(mBitmapShader);

        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);

        mBitmapHeight = mBitmap.getHeight();
        mBitmapWidth = mBitmap.getWidth();

        mBorderRect.set(0, 0, getWidth(), getHeight());
        mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);

        mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
        mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);

        updateShaderMatrix();
        invalidate();
    }

    private void updateShaderMatrix() {
        float scale;
        float dx = 0;
        float dy = 0;

        mShaderMatrix.set(null);

        if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
            scale = mDrawableRect.height() / mBitmapHeight;
            dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
        } else {
            scale = mDrawableRect.width() / mBitmapWidth;
            dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
        }

        mShaderMatrix.setScale(scale, scale);
        mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);

        mBitmapShader.setLocalMatrix(mShaderMatrix);
    }

}
View Code

 

2.2.在values中新建一个资源文件==>attrs_CircleImageView.xml  

<resources>

    <!-- 圆形头像 -->
    <declare-styleable name="CircleImageView">
        <attr name="border_width" format="dimension" />
        <attr name="border_color" format="color" />
    </declare-styleable>

</resources>
View Code

 

2.3.布局中将ImageView视图换成自定义类,路径写自己的

  

  

 

2.4.大功告成!然后图片就乖乖地变成圆形了。如果想了解源码,请看下方的分析。


3.CircleImageView源代码分析

3.1.成员变量分析

  

  ScaleType是图片显示方式。可以参考一下这篇文章了解ScaleType。

  可以有八种取值方式:

  ①.matrix==>表示原图从ImageView的左上角开始绘制。

  ②.fitXY==>填充整个ImageView,需要对图片进行一些缩放,会变形。

  ③.fitStart==>按比例缩放至View的宽度或者高度(取最小值),然后居上或者居左显示。

  ④.fitCenter==>将图片按比例缩放之后居中显示。

  ⑤.fitEnd==>按比例缩放之后居右或者居下显示。

  ⑥.center==>将原图按照原来的大小居中显示,如果超出ImageView的大小,剪裁掉多余的部分。

  ⑦.centerCrop==>将ImageView填充满,按照比例缩放原图,多余的宽和高裁剪掉,最常用的。

  ⑧.centerInside==>将原图完整的显示出来,按照比例缩放原图,一般都变得很小了。

 

  Bitmap.Config是什么东西呢?可以参考一下这篇文章了解Bitmap.Config。

  其实这都是色彩的存储方法,我们知道ARGB指的是一种色彩模式。

  里面A代表Alpha,R表示Red,G表示Green,B表示Blue。每个原色都存储着所表示的颜色的信息值。

  

  位图位数越高代表其可以存储的颜色信息越多,当然图像也就越逼真。

 

  这里定义了一个COLORDEAWABLE_DIMESION用来干什么呢?

  

 

  然后又定义了一个DEFAULT_BORDER_WIDTH,用来干啥呢?

  首先了解一下TypedArray,参考一下这篇文章。

  首先需要有一个资源文件,就是自定义的视图布局,这里是attrs_CircleImageView.xml

  

    然后这里用到了DEFAULT_BORDER_WIDTH了。

  

 

  了解一下RectF,参考一下这篇文章了解RectF。

  RectF类和Rect类似,但是RectF参数是传的Float,所以尾巴有个F了。

  

 

  可以参考一下这篇文章了解Matrix。

  这其实就是一个三维矩阵。

  

 

  然后主要作用分成4块。

  

   用到的方法有:

  

 

  

  postTranslate是指在setScale后平移。

  由于缩放是以(0,0)为中心,所以为了把界面的中心与(0,0)对齐,调用postTranslate(centerX,centerY)把

  图片向这(x,y)方向移动。

 

  什么是Paint类呢?参考这篇文章详细了解。

  这个类可以画集合图形,文本和Bitmap。

  

  什么是BitmapShader呢?参考这篇文章详细了解。

  就是处理图片渲染的。可以做到这样的效果。

  

 

  然后定义了两个整型数据,两个浮点型,两个boolean型,之后再分析作用。

  

 

3.2.构造函数分析

  一个参数的构造函数

  

 

  两个参数的构造函数

  

 

  三个参数的构造函数

  

  这个是最关键的一个构造函数了。

  将资源文件中的宽度和颜色获取到。

  然后调用setup()函数进行初始化。

 

3.3.重写函数getScaleType

  

  

3.4.重写函数setScaleType

  

 

3.5.重写onDraw

  

  这里用canvas画了两个圆。

  android中对于Canvas.drawCircle()方法不理解的可以参考这篇文章。

 

3.6.重写onSizeChanged

  

 

3.7.边界颜色  

  

 

3.8.边界宽度

  

  调用了自己写的一个setup()函数。

 

3.9.重写setImageBitmap

  

  调用了自己写的一个setup()函数。 

 

3.10.重写setImageDrawable

  

  调用了自己写的一个setup()函数。

 

3.11.重写setImageResource

  

  调用了自己写的一个setup()函数。

 

3.12.将Drawable转换成Bitmap

  

 

3.13.自己写的setup函数

  

 

3.14.更新渲染

  


4.其他自定义圆形图片

4.1.Android开发之自定义圆形的ImageView的实现

  效果如下:

  

  自定类代码如下:

/**
 * 自定义的圆形ImageView,可以直接当组件在布局中使用。
 * @author caizhiming
 *
 */
public class XCRoundImageView extends ImageView{

    private Paint paint ;
    
    public XCRoundImageView(Context context) {  
        this(context,null);  
    }  
  
    public XCRoundImageView(Context context, AttributeSet attrs) {  
        this(context, attrs,0);  
    }  
  
    public XCRoundImageView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle); 
        paint = new Paint();
        
    }  
  
    /**
     * 绘制圆形图片
     * @author caizhiming
     */
    @Override  
    protected void onDraw(Canvas canvas) {  
  
        Drawable drawable = getDrawable();  
        if (null != drawable) {  
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();  
            Bitmap b = getCircleBitmap(bitmap, 14);  
            final Rect rectSrc = new Rect(0, 0, b.getWidth(), b.getHeight());  
            final Rect rectDest = new Rect(0,0,getWidth(),getHeight());
            paint.reset();  
            canvas.drawBitmap(b, rectSrc, rectDest, paint);  
  
        } else {  
            super.onDraw(canvas);  
        }  
    }  
  
    /**
     * 获取圆形图片方法
     * @param bitmap
     * @param pixels
     * @return Bitmap
     * @author caizhiming
     */
    private Bitmap getCircleBitmap(Bitmap bitmap, int pixels) {  
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),  
                bitmap.getHeight(), Config.ARGB_8888);  
        Canvas canvas = new Canvas(output);  
          
        final int color = 0xff424242;
       
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());  
        paint.setAntiAlias(true);  
        canvas.drawARGB(0, 0, 0, 0);  
        paint.setColor(color);  
        int x = bitmap.getWidth(); 
        
        canvas.drawCircle(x / 2, x / 2, x / 2, paint);  
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));  
        canvas.drawBitmap(bitmap, rect, rect, paint);  
        return output;  
        
        
    }  
}  
View Code

 

4.2.第三方库圆形头像CircleImageView的使用

  效果如下:

  

  用法如下:

  

 

4.3.自定义ImageView系列——简单圆形图片

  效果如下:

  

  源代码:

public class CircleImageView extends ImageView {

    //基本的三个构造函数
    public CircleImageView(Context context) {
        super(context);
    }

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

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

    //自定义View实现过程中很重要的onDraw绘制图形的方法
    @Override
    protected void onDraw(Canvas canvas) {

        Drawable drawable = getDrawable();

        //空值判断,必要步骤,避免由于没有设置src导致的异常错误
        if (drawable == null) {
            return;
        }

        //必要步骤,避免由于初始化之前导致的异常错误
        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }

        if (!(drawable instanceof BitmapDrawable)) {
            return;
        }
        Bitmap b = ((BitmapDrawable) drawable).getBitmap();

        if (null == b) {
            return;
        }

        Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

        int w = getWidth();

        Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
        canvas.drawBitmap(roundBitmap, 0, 0, null);

    }

    /**
     * 初始Bitmap对象的缩放裁剪过程
     * @param bmp       初始Bitmap对象
     * @param radius    圆形图片直径大小
     * @return 返回一个圆形的缩放裁剪过后的Bitmap对象
     */
    public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
        Bitmap sbmp;
        //比较初始Bitmap宽高和给定的圆形直径,判断是否需要缩放裁剪Bitmap对象
        if (bmp.getWidth() != radius || bmp.getHeight() != radius)
            sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
        else
            sbmp = bmp;
        Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(),
                Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());

        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(Color.parseColor("#BAB399"));
        canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f,
                sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f, paint);
        //核心部分,设置两张图片的相交模式,在这里就是上面绘制的Circle和下面绘制的Bitmap
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(sbmp, rect, rect, paint);

        return output;
    }

}
View Code


posted @ 2017-11-29 11:17  Jason_Jan  阅读(5946)  评论(0编辑  收藏  举报