项目中有一个需求需要完成跟手异形切割动效,大概的效果如下
整体思路:由于圆角和路径效果非常规,无法使用outline方法实现,于是使用Path勾勒出切割区域,然后在dispatchDraw方法中切割canvas.clipPath指定绘制的区域,同时根据手势位置动态更新路径的参数,不断循环路径更新+重绘,从而实现跟手的异性切割效果。
1.路径点绘制顺序
2.使用代码勾勒路径
使用Path.moveTo/lineTo/arcTo等方法勾勒出路径
class XXLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private var clipTop: Int = 0 private var clipEnd: Int = 0
private var clipBottom: Int = 0
private var clipCornerRadius = xx
private val leftOrRightMargin = xx
...
init {
val defaultDisplayContext = DisplayUtil.getDefaultDisplayContext(context) ?: context
val defaultTypedArray =
defaultDisplayContext.obtainStyledAttributes(attrs, R.styleable.xx)
clipRadius =
defaultTypedArray.getDimensionPixelSize(R.styleable.xx, 0)
clipStart =
defaultTypedArray.getDimensionPixelSize(R.styleable.xx, 0)
clipEnd =
defaultTypedArray.getDimensionPixelSize(R.styleable.xx, 0)
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.xx)
clipTop = typedArray.getDimensionPixelSize(R.styleable.xx, 0)
defaultTypedArray.recycle()
typedArray.recycle()
}
...
private fun updateClipPath() {
val clipStart = clipStart
val clipEnd = width - clipEnd
mPath.rewind()
mPathRect.rewind()
val clipRectMarginTest = 0f//方便调试
//外切割矩形参数
val clipRectLeft = clipStart + clipRectMarginTest
val clipRectTop = clipTop.toFloat()
val clipRectRight = clipEnd - clipRectMarginTest
val clipRectBottom = clipBottom.toFloat()
val clipRectBottomInner = clipRectBottom - clipCornerRadius
//内切割矩形参数,卡片列表真实区域
val clipCornerLeft = leftOrRightMargin
val clipCornerRight = width - leftOrRightMargin
val clipCornerBottom = clipRectBottom
mPath.moveTo(clipRectLeft, clipRectTop)//路径点1
mPath.lineTo(clipRectLeft, clipRectBottomInner)//路径点2
mPath.lineTo(clipCornerLeft, clipRectBottomInner)//路径点3
//左下圆角
mPath.arcTo(
RectF(
clipCornerLeft,
clipCornerBottom - clipCornerRadius * 2,
clipCornerLeft + clipCornerRadius * 2,
clipCornerBottom
), ROUNDRECT_LEFT_START_ANGEL, ROUNDRECT_LEFT_END_ANGEL, false
)
mPath.lineTo(clipCornerRight - clipCornerRadius, clipRectBottom)//路径点5
//右下圆角
mPath.arcTo(
RectF(
clipCornerRight - clipCornerRadius * 2,
clipCornerBottom - clipCornerRadius * 2,
clipCornerRight,
clipCornerBottom
), ROUNDRECT_RIGHT_START_ANGEL, ROUNDRECT_RIGHT_END_ANGEL, false
)
mPath.lineTo(clipRectRight, clipRectBottomInner)//路径点7
mPath.lineTo(clipRectRight, clipRectTop)//路径点8
mPath.lineTo(clipRectLeft, clipRectTop)//路径点1
}
...
}
3.限制绘制区域实现切割效果
override fun dispatchDraw(canvas: Canvas) {
if (enableClipPath && clipRegionHasSet) {
updateClipPath()
canvas?.clipPath(mPath)//将canvas裁剪到path设定的区域,往后的绘制都只能在此区域中
}
super.dispatchDraw(canvas)
}