关于自定义画板View针对压感进行处理

关于自定义画板View针对压感进行处理

最近有一个需求是针对手写笔的压感对绘画的线条进行粗细变化,最后实现之后发现起始原理也挺简单,但是找的过程兼职折磨。

话不多说先上代码链接自定义画板,接下来是简单的代码解析:

1、简单介绍:

画板使用的是双缓冲模式即自定义一个画板然后使用一个Bitmap为底先在Bitmap上绘制再在自定义View的onDraw方法中对Bitmap进行绘制,这样可以减少后期因为Path过多导致onDraw的绘画卡顿问题。

2、关于压感:

因为要防止画笔的粗细变化过大所以引入敏感度值来进行过度,下面是笔触的计算方法:

private fun calculateStrokeWidth(pressure: Float, maxStrokeWidth: Float): Float {
        // 平滑压感变化
        lastPressure += (pressure - lastPressure) * 0.3f // 0.3f 是敏感度参数
        // 计算笔触宽度(不超过最大宽度)
        return (lastPressure * maxStrokeWidth).coerceAtMost(maxStrokeWidth)
}

根据我的查询Android的Path原生是不支持不同位置的不同粗细策略,然后查询了不少之后发现Android这边可以使用PathMeasure对Path进行截取,所以我这边给出来的策略是在onTouchEvent的Action_MOVE中对完整的Path进行分段截取然后改变Paint的粗细之后进行绘画实现压感的效果,以下是相关代码:

private fun touchMove(x: Float, y: Float) {
        penPaint.strokeWidth = calculateStrokeWidth(pressure, maxStrokeWidth)
        eraserPaint.strokeWidth = calculateStrokeWidth(pressure, maxEraserWidth)
        val dx = abs(x - mX)
        val dy = abs(y - mY)
        val newPath = Path()
        if (dx >= touchTolerance || dy >= touchTolerance) {
            mainPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2)
            pathMeasure.setPath(mainPath, false)
            val length = pathMeasure.length
            // 获取 mainPath 上相应的段落
            pathMeasure.getSegment(
                lastSegmentEnd,
                length,
                newPath,
                true
            )
            lastSegmentEnd = length
            mX = x
            mY = y
        }
        canvas.drawPath(
            newPath,
            if (commentTypeEnum == CommentTypeEnum.DRAW_PEN_PATH) penPaint else eraserPaint
        )
    }

一开始本来是想着每次画完之后重置主Path然后移动到之前的位置重新绘画的,强制分段,但是发现这样画出来的线是堪称完美的虚线,然后就放弃了。

至此针对压感实现绘画进行粗细变化的效果就完成了,感谢。