感谢菜鸟教程提供的教程以及案例。本文根据是根据案例进行的自我实现。(弱引用什么的会影响新手理解)
8.3.6 Paint API之—— Xfermode与PorterDuff详解(三) | 菜鸟教程 (runoob.com)
实现方式:通过两个图层,图层A绘制图片,图层B绘制遮罩层,然后以相交模式将B放在A上,即可实现圆形图片。主要是针对
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));的实际应用。
讲解:本文中使用到三个画布canvas,分别是整个视图的画布函数形参canvasA,图片画图canvasB,遮罩层canvasC。主要实现是先绘制B,再设置画笔格式为DST_IN,然后在B上绘制C,最后把B绘制到A上。
记录一下:mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
关于两个图层的层叠方式,共有18种。
DST 是先绘制的图层A,SRC是后绘制的图B。使用DST_IN表示将A和B的相交部分绘制为A的图层,其余部分不显示。所以使用DST_IN要先绘制照片,后绘制遮罩。如果先绘制遮罩后绘制图片就需要使用SRC_IN。
代码实现:(记得在ondraw方法中去掉父类的构造方法,否则会重复绘制)
public class MyCircleView extends androidx.appcompat.widget.AppCompatImageView {
Paint mPaint;
public MyCircleView(Context context) {
super(context);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
public MyCircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
public MyCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//取短边为范围
int size=Math.min(getMeasuredHeight(),getMeasuredWidth());
setMeasuredDimension(size,size);
}
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
// 获取图片的宽高
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
float scale=getWidth()*1.0f/Math.min(width,height);
if (scale>1){
drawable.setBounds(0, 0, (int) (scale * width), (int) (scale * height));
}
//先拿一块画布绘制图片,设置画笔模式,再拿一块画布绘制遮罩
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas drawCanvas = new Canvas(bitmap);
drawable.draw(drawCanvas);
Bitmap bitmap2 = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas1 = new Canvas(bitmap2);
Paint paint = new Paint();
canvas1.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, paint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
drawCanvas.drawBitmap(bitmap2,0,0,mPaint);
canvas.drawBitmap(bitmap,0,0,null);
mPaint.setXfermode(null);
}
}
除此之外,你可以在res/attr.xml文件下给自定义的view设置xml输入属性。然后在构造函数中获取这些属性并操作。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCircleView "> <!-- 你自定义的view名称 -->
<attr name="Radius" format="dimension"/>
<attr name="type">
<enum name="circle" value="0"/>
<enum name="round" value="1"/>
</attr>
</declare-styleable>
</resources>
//在构造函数中,通过上下文context获取
public CircleImageView(Context context, AttributeSet attrs) {
TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
int mBorderRadius = tArray.getDimensionPixelSize(R.styleable.CircleImageView_Radius, BODER_RADIUS_DEFAULT);
int type = tArray.getInt(R.styleable.CircleImageView_type, TYPE_CIRCLE);
tArray.recycle();//释放资源,建议是把属性赋值给全局变量后,就释放掉这个资源。
}