本文参考自大神张鸿洋的文章,同时修复了一个我自己发现的问题(因为不知道算不算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.
* 邮箱:214525789@qq.com
*/
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;
}
}