Android 利用 Xfermode 来实现圆形头像,矩形描边 View

先上一个 Xfermode 整体的图片

Android 利用 Xfermode 来实现圆形头像,矩形描边 View_第1张图片

实现方法如下:
通过新建涂层,先将外部的边框(半径为图片一半减去描边的宽度)画出来,参考 PorterDuff.Mode.DST_OUT 模式

Android 利用 Xfermode 来实现圆形头像,矩形描边 View_第2张图片

然后将新建的图层和 自身图片合并之后,再通过 外层实现 mCircleShape 将多余的边框裁减掉,参考 PorterDuff.Mode.DST_IN 模式
Android 利用 Xfermode 来实现圆形头像,矩形描边 View_第3张图片

自定义属性参考如下:

自定义属性 默认值 描述
shapeMode circle 边框形状
roundRadius 0 描边弧度
strokeWidth 0 描边宽度
strokeColor 0x000000 描边颜色

最后附上整体代码:

/**
 * 
 * PackageName:  com.example.kotlin.utils
 * Description:  带边框的 imageView
 * Created by :  Liu
 * date:         2017/12/27
 * 
*/
public class StrokeImageView extends AppCompatImageView { public static final int SHAPE_MODE_ROUND_RECT = 1; public static final int SHAPE_MODE_CIRCLE = 0; private int mShapeMode = 0; // 描边的形状 private float mRadius = 0; private int mStrokeColor = 0x000000; private float mStrokeWidth = 0; private boolean mShapeChanged; private Shape mMainShape;// 内部的圆形 private Shape mCircleShape;// 描边的圆形 private Paint mMainPaint, mStrokePaint; private Bitmap mStrokeBitmap; public StrokeImageView(Context context) { this(context, null); } public StrokeImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StrokeImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } private void init(AttributeSet attrs) { setLayerType(LAYER_TYPE_HARDWARE, null);// 硬件加速建议关闭 if (attrs != null) { TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.StrokeImageView); mShapeMode = a.getInt(R.styleable.StrokeImageView_shapeMode, 0); mRadius = a.getDimension(R.styleable.StrokeImageView_roundRadius, 0); mStrokeWidth = a.getDimension(R.styleable.StrokeImageView_strokeWidth, 0); mStrokeColor = a.getColor(R.styleable.StrokeImageView_strokeColor, mStrokeColor); a.recycle(); } mMainPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMainPaint.setFilterBitmap(true); mMainPaint.setColor(Color.GREEN); mMainPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));// PorterDuff.Mode.DST_IN 显示相交的 DST部分,将多余的边框裁剪掉 mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mStrokePaint.setFilterBitmap(true); mStrokePaint.setColor(Color.RED); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed || mShapeChanged) { mShapeChanged = false; int width = getMeasuredWidth(); int height = getMeasuredHeight(); switch (mShapeMode) { case SHAPE_MODE_ROUND_RECT: break; case SHAPE_MODE_CIRCLE: int min = Math.min(width, height); mRadius = (float) min / 2; break; } if (mMainShape == null || mRadius != 0) { float[] radius = new float[8]; Arrays.fill(radius, mRadius); mMainShape = new RoundRectShape(radius, null, null); mCircleShape = new RoundRectShape(radius, null, null); } mMainShape.resize(width, height); mCircleShape.resize(width - mStrokeWidth * 2, height - mStrokeWidth * 2); createStrokeBitmap(); } } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mStrokeWidth > 0 && mCircleShape != null && mStrokeBitmap != null) { int i = canvas.saveLayer(0, 0, getMeasuredWidth(), getMeasuredHeight(), null, Canvas.ALL_SAVE_FLAG); mStrokePaint.setXfermode(null); canvas.drawBitmap(mStrokeBitmap, 0, 0, mStrokePaint); canvas.translate(mStrokeWidth, mStrokeWidth); mStrokePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));// 以显示的中心圆向外画出要描边的矩形边框 mCircleShape.draw(canvas, mStrokePaint); canvas.restoreToCount(i); } switch (mShapeMode) { case SHAPE_MODE_ROUND_RECT: case SHAPE_MODE_CIRCLE: if (mMainShape != null) { mMainShape.draw(canvas, mMainPaint); } break; } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mStrokeBitmap == null) createStrokeBitmap(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); releaseStrokeBitmap(); } private Bitmap createStrokeBitmap() { if (mStrokeWidth <= 0) return null; int w = getMeasuredWidth(); int h = getMeasuredHeight(); if (w == 0 || h == 0) return null; releaseStrokeBitmap(); mStrokeBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(mStrokeBitmap); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(mStrokeColor); c.drawRect(new RectF(0, 0, w, h), p); return mStrokeBitmap; } private void releaseStrokeBitmap() { if (mStrokeBitmap != null) { mStrokeBitmap.recycle(); mStrokeBitmap = null; } } public void setStroke(int strokeColor, float strokeWidth) { if (mStrokeWidth <= 0) return; if (mStrokeWidth != strokeWidth) { mStrokeWidth = strokeWidth; int width = getMeasuredWidth(); int height = getMeasuredHeight(); if(mCircleShape != null) { mCircleShape.resize(width - mStrokeWidth * 2, height - mStrokeWidth * 2); } postInvalidate(); } if (mStrokeColor != strokeColor) { mStrokeColor = strokeColor; createStrokeBitmap(); postInvalidate(); } } public void setStrokeColor(int strokeColor) { setStroke(strokeColor, mStrokeWidth); } public void setStrokeWidth(float strokeWidth) { setStroke(mStrokeColor, strokeWidth); } public void setShape(int shapeMode, float radius) { mShapeChanged = mShapeMode != shapeMode || mRadius != radius; if (mShapeChanged) { mShapeMode = shapeMode; mRadius = radius; mMainShape = null; mCircleShape = null; requestLayout(); } } public void setShapeMode(int shapeMode) { setShape(shapeMode, mRadius); } public void setShapeRadius(float radius) { setShape(mShapeMode, radius); } }

自定义属性 :

"StrokeImageView">

        "shapeMode" format="enum"> // 描边形状,圆形 or 矩形
            "circle" value="0" />
            "roundRect" value="1" />
        

        "roundRadius" format="dimension" /> // 描边弧度
        "strokeWidth" format="dimension" /> // 描边宽度
        "strokeColor" format="color" /> //  描边的颜色
    

XML 中使用方法

<com.example.kotlin.utils.StrokeImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@mipmap/ball"
    app:shapeMode="circle"
    app:strokeWidth="2dp"
    app:strokeColor="@android:color/holo_red_light"/>

你可能感兴趣的:(ui基础)