安卓View学习

自定义圆形ImageView

2018-02-02  本文已影响8人  sankemao

自定义圆形ImageView,一般来讲有4中方式

  1. BitmapShader(可直接操控bitmap的渲染器,将画笔用bitmap图形填充)
  2. Xfermode
  3. 继承drawable
  4. clipPath

本文将采用第二种方式实现圆形图片,网上也有许多圆形图片的写法,我看了下大多是继承 ImageView并完全重写了onDraw()方法,导致在xml中设置图片的scaleType失效,或者和Glide结合使用的时候出现莫名奇妙的问题。所以这个控件保留了onDraw()当中的super.onDraw()方法,也就避开了以上的情况。

先看一下效果图:


image.png

思路:
主要是利用Paint的PorterDuffXfermode,一共16中模式,这里从网上找了张图


image.png

需要绘制两次:

  1. 先调用super.onDraw()绘制原本的imageView,
  2. 设置画笔模式为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();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读