美文网首页安卓View学习
自定义圆形ImageView

自定义圆形ImageView

作者: sankemao | 来源:发表于2018-02-02 16:58 被阅读8次

    自定义圆形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();
        }
    }
    

    相关文章

      网友评论

        本文标题:自定义圆形ImageView

        本文链接:https://www.haomeiwen.com/subject/wyuezxtx.html