该自定义View继承自ImageView,该自定义View和ImageView的区别就是ImageView是正方形的,而自定义View是圆形的,园外都是一些透明的背景,因此我们就不需要再重写onMeasure这个方法,直接重写onDraw方法即可。
直接上代码
public class RoundImageView extends ImageView {
private Context context ;
private int radius = 0 ;
private int defaultWidth = 0 , defaultHeight = 0 ;
public RoundImageView(Context context) {
super(context);
this.context = context ;
}
public RoundImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context ;
}
public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context ;
}
/**
* 获取裁剪后的圆形图片
* @param radius 半径
*/
private Bitmap getRoundBitmap (Bitmap bitmap ,int radius) {
Bitmap squareBitmap;
int diameter = radius * 2;//直径
int x = 0, y = 0;
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int squareWidth = 0, squareHeight = 0;
//以bitmap的短的边的标准,截取一个以bitmap的中心为中心的正方形
if (bitmapWidth > bitmapHeight) {
x = bitmapWidth / 2 - bitmapHeight / 2;
y = 0;
squareHeight = squareWidth = bitmapHeight;
} else {
y = bitmapHeight / 2 - bitmapWidth / 2;
x = 0;
squareHeight = squareWidth = bitmapWidth;
}
squareBitmap = Bitmap.createBitmap(bitmap, x, y, squareWidth, squareHeight);
//将squareBitmap缩放成diameter的大小
squareBitmap = Bitmap.createScaledBitmap(squareBitmap, diameter, diameter, false);
//新建一个bitmap resultbitmap,大小为squareBitmap的大小
Bitmap resultbitmap = Bitmap.createBitmap(squareBitmap.getWidth(),
squareBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(resultbitmap);
Paint paint = new Paint();
//新建一个矩形区域位置为0, 0, squareBitmap.getWidth(),squareBitmap.getHeight()
Rect rect = new Rect(0, 0, squareBitmap.getWidth(),squareBitmap.getHeight());
paint.setAntiAlias(true);//设置抗锯齿
paint.setFilterBitmap(true);//对位图进行滤波处理
paint.setDither(true);//设置防抖动
canvas.drawARGB(0, 0, 0, 0);//画背景颜色为透明
//以(squareBitmap.getWidth() / 2,squareBitmap.getHeight() / 2)为圆心,squareBitmap.getWidth() / 2为半径画圆
canvas.drawCircle(squareBitmap.getWidth() / 2,
squareBitmap.getHeight() / 2,
squareBitmap.getWidth() / 2,
paint);
//精髓,因为这句话,canvas原先画的透明背景圆和接下来的squareBitmap重叠部分显示出来
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//在画布中将squareBitmap画出来
canvas.drawBitmap(squareBitmap, rect, rect, paint);
bitmap = null ;
squareBitmap = null;
//将画布中的内容返回
return resultbitmap;
}
@Override
protected void onDraw(Canvas canvas) {
//获取图片
Drawable drawable = getDrawable() ;
//如果图片为空,直接放返回
if (drawable == null)
return;
//如果控件的宽高其中一个为0,直接返回
if (getWidth() == 0 || getHeight() == 0)
return;
this.measure(0, 0);
//如果是.9图也无法显示,直接返回
if(drawable.getClass() == NinePatchDrawable.class)
return;
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
//获取控件的宽高
if (defaultWidth == 0) {
defaultWidth = getWidth();
}
if (defaultHeight == 0) {
defaultHeight = getHeight();
}
//半径的等于宽高中较小的一边的一半
radius = (defaultHeight > defaultWidth ? defaultWidth :defaultHeight )/2;
Bitmap roundBitmap = getRoundBitmap(bitmap, radius);
//以控件的中心为中心画出图片
canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight / 2 - radius, null);
}
}
重点来说getRoundBitmap 中精髓的那一句
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
在这句话中的PorterDuffXfermode有很多中,他们代表着两张图片相交时的模式,下图可以直观显示他们的不同之处
假设SRC为原图,DST为目标图
PorterDuffXfermode
利用这个我们还可以自定义一些圆形的进度条等等