Android 使用PorterDuff.Mode.XOR自定义透明遮罩

    在业务开发中很多场景都会遇到遮罩。今天使用 PorterDuffXfermode 的XOR模式来绘制一个中间透明的遮罩。

先上一张图看看效果

分析一下:

1:后面的背景是黑色

2:中间还有一个不规则的透明部分 使中间透明就需要使用到 PorterDuff.Mode.XOR

3:还有一个边框

以上如何实现的呢?下面上代码

一:首先继承View

class MaskView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {




}

二:初始化需要的参数

    // 视图宽高
    private var viewWith: Int = 0
    private var viewHeight: Int = 0

    // 画笔:默认画笔  背景画笔  前景(透明部分)画笔  边框画笔
    private lateinit var defaultPaint: Paint
    private lateinit var bgPaint: Paint
    private lateinit var fgPaint: Paint
    private lateinit var borderPaint: Paint

    // 路径
    private lateinit var fgPath: Path
    private lateinit var borderPath: Path

    // 前景上边线的margin值
    private var fgMargin = 100f

    // kotlin 初始化方法
    init {
        initPaint()
    }

    /**
     *  初始化画笔
     */
    private fun initPaint() {
        defaultPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        // 黑色背景画笔
        bgPaint = Paint(defaultPaint).apply {
            color = Color.BLACK
        }
        // 透明区域画笔
        fgPaint = Paint(defaultPaint).apply {
            xfermode = PorterDuffXfermode(PorterDuff.Mode.XOR)
        }
        // 边框画笔
        borderPaint = Paint(defaultPaint).apply {
            color = Color.parseColor("#00BECC")
            style = Paint.Style.STROKE
            strokeWidth = 10f
        }
    }

三:获取视图的宽高

    /**
     *  测量
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val w = MeasureSpec.getSize(widthMeasureSpec)
        val h = MeasureSpec.getSize(heightMeasureSpec)
        resetViewSize(w, h)
    }

    /**
     *  大小发生改变
     */
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        resetViewSize(w, h)
    }

    /**
     *  重新设定视图大小
     */
    private fun resetViewSize(w: Int, h: Int) {
        // 设置该view的大小
        setMeasuredDimension(viewWith, viewHeight)

        // 宽高保存为全局变量
        viewWith = w
        viewHeight = h

        // 获取前景的path路径
        fgPath = getPath(true)
        borderPath = getPath(false)

        // 重新绘制Canvas
        invalidate()
    }

四:获取路径

    /**
     *  获取绘制的Path路径
     *  @param isWinding 是否全填充
     */
    private fun getPath(isWinding: Boolean) = Path().apply {
        reset()
        // 第一条线起点
        moveTo(fgMargin, 10f)

        // 第一条线的终点 (顶部)
        lineTo(viewWith - fgMargin, 10f)

        // 第二条线的结束点 (右边向中部走)
        lineTo(viewWith.toFloat() - 10f, viewHeight / 2f)

        // 第三条线 (右边中部向下走)
        lineTo(viewWith - fgMargin, viewHeight.toFloat() - 10f)

        // 第四条线 (底部)
        lineTo(fgMargin, viewHeight.toFloat() - 10f)

        // 第五条线 (左边底部)
        lineTo(10f, viewHeight / 2f)

        // 最后一条回到起点
        lineTo(fgMargin, 10f)

        // 闭合
        close()

        // 如果是背景那就全填充
        if (isWinding) {
            fillType = Path.FillType.WINDING
        }
    }

说明

  这是path 绘制的路径,isWinding 来判断是否填充,因为使用了XOR模式的Paint 所以绘制出来就是透明的,边框也是用的是这个路径所以边框不需要填充   

 五:绘制

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        // 保存之前的图层 !!!很重要
        canvas?.saveLayer(0f, 0f, viewWith.toFloat(), viewHeight.toFloat(), defaultPaint)
        // 绘制背景
        canvas?.drawRect(0f, 0f, viewWith.toFloat(), viewHeight.toFloat(), bgPaint)
        // 绘制前景(透明区域) 因为用到了XOR模式所以绘制的区域会变成透明的
        canvas?.drawPath(fgPath, fgPaint)
        // 保存图层
        canvas?.saveLayer(0f, 0f, viewWith.toFloat(), viewHeight.toFloat(), defaultPaint)
        // 绘制边框
        canvas?.drawPath(borderPath, borderPaint)
    }

 注意:记得保存图层!

需要源码点击这里

代码很简单,希望可以帮助到你。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值