最近有一个需求是针对手写笔的压感对绘画的线条进行粗细变化,最后实现之后发现起始原理也挺简单,但是找的过程兼职折磨。
话不多说先上代码链接自定义画板,接下来是简单的代码解析:
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然后移动到之前的位置重新绘画的,强制分段,但是发现这样画出来的线是堪称完美的虚线,然后就放弃了。
至此针对压感实现绘画进行粗细变化的效果就完成了,感谢。