Android 自定义圆形图片 CircleImageView

时间:2023-03-09 05:55:15
Android 自定义圆形图片 CircleImageView

1.效果预览

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

  Android 自定义圆形图片 CircleImageView

1.2.然后看一看图片效果

  Android 自定义圆形图片 CircleImageView

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

  Android 自定义圆形图片 CircleImageView

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);
} }

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>

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

  Android 自定义圆形图片 CircleImageView

  Android 自定义圆形图片 CircleImageView

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

3.CircleImageView源代码分析

3.1.成员变量分析

  Android 自定义圆形图片 CircleImageView

  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。每个原色都存储着所表示的颜色的信息值。

  Android 自定义圆形图片 CircleImageView

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

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

  Android 自定义圆形图片 CircleImageView

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

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

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

  Android 自定义圆形图片 CircleImageView

   然后这里用到了DEFAULT_BORDER_WIDTH了。

  Android 自定义圆形图片 CircleImageView

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

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

  Android 自定义圆形图片 CircleImageView

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

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

  Android 自定义圆形图片 CircleImageView

  然后主要作用分成4块。

  Android 自定义圆形图片 CircleImageView

  用到的方法有:

  Android 自定义圆形图片 CircleImageView

  Android 自定义圆形图片 CircleImageView

  postTranslate是指在setScale后平移。

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

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

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

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

  

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

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

  Android 自定义圆形图片 CircleImageView

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

  Android 自定义圆形图片 CircleImageView

3.2.构造函数分析

  一个参数的构造函数

  Android 自定义圆形图片 CircleImageView

  两个参数的构造函数

  Android 自定义圆形图片 CircleImageView

  三个参数的构造函数

  Android 自定义圆形图片 CircleImageView

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

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

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

3.3.重写函数getScaleType

  Android 自定义圆形图片 CircleImageView

  

3.4.重写函数setScaleType

  Android 自定义圆形图片 CircleImageView

3.5.重写onDraw

  Android 自定义圆形图片 CircleImageView

  这里用canvas画了两个圆。

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

3.6.重写onSizeChanged

  Android 自定义圆形图片 CircleImageView

3.7.边界颜色  

  Android 自定义圆形图片 CircleImageView

3.8.边界宽度

  Android 自定义圆形图片 CircleImageView

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

3.9.重写setImageBitmap

  Android 自定义圆形图片 CircleImageView

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

3.10.重写setImageDrawable

  Android 自定义圆形图片 CircleImageView

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

3.11.重写setImageResource

  Android 自定义圆形图片 CircleImageView

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

3.12.将Drawable转换成Bitmap

  Android 自定义圆形图片 CircleImageView

3.13.自己写的setup函数

  Android 自定义圆形图片 CircleImageView

3.14.更新渲染

  Android 自定义圆形图片 CircleImageView

4.其他自定义圆形图片

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

  效果如下:

  Android 自定义圆形图片 CircleImageView

  自定类代码如下:

/**
* 自定义的圆形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; }
}

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

  效果如下:

  Android 自定义圆形图片 CircleImageView

  用法如下:

  Android 自定义圆形图片 CircleImageView

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

  效果如下:

  Android 自定义圆形图片 CircleImageView

  源代码:

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;
} }