自定义圆形ImageView,一般来讲有4中方式
- BitmapShader(可直接操控bitmap的渲染器,将画笔用bitmap图形填充)
- Xfermode
- 继承drawable
- clipPath
本文将采用第二种方式实现圆形图片,网上也有许多圆形图片的写法,我看了下大多是继承 ImageView并完全重写了onDraw()方法,导致在xml中设置图片的scaleType失效,或者和Glide结合使用的时候出现莫名奇妙的问题。所以这个控件保留了onDraw()当中的super.onDraw()方法,也就避开了以上的情况。
先看一下效果图:
image.png
思路:
主要是利用Paint的PorterDuffXfermode,一共16中模式,这里从网上找了张图
image.png
需要绘制两次:
- 先调用super.onDraw()绘制原本的imageView,
- 设置画笔模式为PorterDuff.Mode.DST_IN,绘制我们想要的图片形状,比如圆形或者圆角矩形等,这个图片形状需要是填充的,所以设置画笔Paint.Style.FILL,至于颜色无所谓,我们只在乎形状。
查看上图的PorterDuff.Mode.DST_IN模式,可以得出最终的绘制结果是两次绘制的交集部分。
需要注意的是,我们不能直接在onDraw中绘制,需要 canvas.saveLayer(mLayer, null, Canvas.ALL_SAVE_FLAG);即新建图层,该图层默认是透明的,所有的操作都在新的图层上, 最后再与原图层合并,就像ps中图层一样。如果直接在默认图层上绘制,你会发现默认图层的背景不是透明的,对最终结果有污染。
源码,可以拷过去直接用:
<declare-styleable name="XImageView">
<attr name="x_as_circle" format="boolean"/>
<attr name="x_round_corner" format="reference|dimension"/>
<attr name="x_round_top_left" format="reference|dimension"/>
<attr name="x_round_top_right" format="reference|dimension"/>
<attr name="x_round_bottom_left" format="reference|dimension"/>
<attr name="x_round_bottom_right" format="reference|dimension"/>
<attr name="x_stroke_color" format="reference|color"/>
<attr name="x_stroke_width" format="reference|dimension"/>
</declare-styleable>
/**
* Description: 圆形ImageView
* Create Time: 2018/1/29.15:00
* Author:jin
* Email:210980059@qq.com
*/
public class XImageView extends AppCompatImageView {
//是否为圆形头像
private boolean mAsCircle;
//圆角半径
private int mRoundCorner;
//描边宽度
private int mStrokeWidth;
//描边颜色
private int mStrokeColor = Color.WHITE;
//绘制描边或者形状的画笔
private Paint mSrcPaint;
//图层区域
private RectF mLayer;
private Path mClipPath;
private int mTopLeft;
private int mTopRight;
private int mBottomLeft;
private int mBottomRight;
//圆角path规则
private float radii[] = new float[8];
public XImageView(Context context) {
this(context, null);
}
public XImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public XImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
mClipPath = new Path();
mSrcPaint = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.DITHER_FLAG);
radii[0] = mTopLeft;
radii[1] = mTopLeft;
radii[2] = mTopRight;
radii[3] = mTopRight;
radii[4] = mBottomRight;
radii[5] = mBottomRight;
radii[6] = mBottomLeft;
radii[7] = mBottomLeft;
}
/**
* 初始化属性
*/
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.XImageView);
mAsCircle = ta.getBoolean(R.styleable.XImageView_x_as_circle, mAsCircle);
mRoundCorner = ta.getDimensionPixelSize(R.styleable.XImageView_x_round_corner, mRoundCorner);
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.XImageView_x_stroke_width, mStrokeWidth);
mStrokeColor = ta.getColor(R.styleable.XImageView_x_stroke_color, mStrokeColor);
mTopLeft = ta.getDimensionPixelSize(R.styleable.XImageView_x_round_top_left, mRoundCorner);
mTopRight = ta.getDimensionPixelSize(R.styleable.XImageView_x_round_top_right, mRoundCorner);
mBottomLeft = ta.getDimensionPixelSize(R.styleable.XImageView_x_round_bottom_left, mRoundCorner);
mBottomRight = ta.getDimensionPixelSize(R.styleable.XImageView_x_round_bottom_right, mRoundCorner);
ta.recycle();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mLayer = new RectF(0, 0, w, h);
mClipPath.reset();
if (mAsCircle) {
float d = Math.min(w, h);
float r = d / 2;
PointF centerPoint = new PointF(w / 2, h / 2);
mClipPath.addCircle(centerPoint.x, centerPoint.y, r, Path.Direction.CW);
mClipPath.moveTo(-10, -10);
mClipPath.moveTo(w + 10, h + 10);
} else {
mClipPath.addRoundRect(mLayer, radii, Path.Direction.CW);
}
}
@Override
protected void onDraw(Canvas canvas) {
//新建图层,以下所有canvas操作都在新图层上,防止Xfermode模式下原图层的污染
canvas.saveLayer(mLayer, null, Canvas.ALL_SAVE_FLAG);
super.onDraw(canvas);
if (mStrokeWidth > 0) {
mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
mSrcPaint.setStrokeWidth(mStrokeWidth * 2);
mSrcPaint.setColor(mStrokeColor);
mSrcPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(mClipPath, mSrcPaint);
}
//取交集,显示dst层
mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mSrcPaint.setColor(Color.WHITE);
mSrcPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(mClipPath, mSrcPaint);
canvas.restore();
}
}
网友评论