使用Path实现Android自定义View圆角时效果不符合预期
解决方案
问题出在CornerPathEffect的特性上——它会对路径上所有转角做统一的圆角处理,包括你特意绘制的左右切角圆弧的端点,直接破坏了原本的切角形状;同时左上角的路径衔接逻辑不对,加上这个效果后就变成了直线。要实现预期的“左右圆形切角+四角圆角”效果,必须手动精确绘制每一段路径,放弃使用CornerPathEffect。
修改后的完整代码
class RoundedRectUsingPath @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val roundCorner = 10f // 四角圆角半径 private val avatarRadius = 30f // 左右切角半径 private val paint = Paint().apply { color = Color.RED isAntiAlias = true setShadowLayer(10f, -2f, 2f, Color.BLACK) style = Paint.Style.FILL // 确保填充,阴影正常显示 } private var path = Path() init { // 自定义Path的阴影需要关闭硬件加速,否则可能不显示 setLayerType(LAYER_TYPE_SOFTWARE, null) } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) val leftOffset = 10f val verticalOffset = 20f // 主体矩形的边界(去掉左右切角占用的空间) val mainRect = RectF( leftOffset + avatarRadius, verticalOffset, w - leftOffset - avatarRadius, h - verticalOffset ) // 左切角的圆心和边界 val leftCutCenter = PointF(leftOffset, mainRect.centerY()) val leftCutRect = fromCircle(leftCutCenter, avatarRadius) // 右切角的圆心和边界 val rightCutCenter = PointF(w - leftOffset, mainRect.centerY()) val rightCutRect = fromCircle(rightCutCenter, avatarRadius) path.reset() path.apply { // 1. 左上角圆角:从圆角的右端点开始,绘制左上角的90度圆弧 moveTo(mainRect.left + roundCorner, mainRect.top) arcTo( RectF( mainRect.left, mainRect.top, mainRect.left + roundCorner * 2, mainRect.top + roundCorner * 2 ), 180f, 90f, false ) // 2. 连接到左切角的上端点,绘制左切角的180度圆弧(从顶部到底部) lineTo(leftCutRect.left, mainRect.top) arcTo(leftCutRect, -90f, 180f, false) // 3. 连接到左下角圆角的上端点,绘制左下角的90度圆弧 lineTo(mainRect.left, mainRect.bottom) arcTo( RectF( mainRect.left, mainRect.bottom - roundCorner * 2, mainRect.left + roundCorner * 2, mainRect.bottom ), 90f, 90f, false ) // 4. 连接到右下角圆角的左端点,绘制右下角的90度圆弧 lineTo(mainRect.right, mainRect.bottom) arcTo( RectF( mainRect.right - roundCorner * 2, mainRect.bottom - roundCorner * 2, mainRect.right, mainRect.bottom ), 180f, 90f, false ) // 5. 连接到右切角的下端点,绘制右切角的180度圆弧(从底部到顶部) lineTo(rightCutRect.right, mainRect.bottom) arcTo(rightCutRect, 90f, 180f, false) // 6. 连接到右上角圆角的下端点,绘制右上角的90度圆弧 lineTo(mainRect.right, mainRect.top) arcTo( RectF( mainRect.right - roundCorner * 2, mainRect.top, mainRect.right, mainRect.top + roundCorner * 2 ), 270f, 90f, false ) // 闭合路径 close() } } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawPath(path, paint) } // 保留你的辅助工具方法 private fun fromLTRB(left: Float, top: Float, right: Float, bottom: Float) = RectF(left, top, right, bottom) private fun fromCircle(center: PointF, radius: Float) = fromLTRB( center.x - radius, center.y - radius, center.x + radius, center.y + radius ) }
关键改动说明
- 移除
CornerPathEffect:放弃统一转角处理,改为手动绘制每个圆角和切角,完全控制形状。 - 拆分路径逻辑:把路径拆分为「四角圆角+左右切角」的分段绘制,确保每一段衔接无缝,每个圆弧的角度和范围都精确对应。
- 调整主体矩形边界:把主体矩形向中间收缩,留出左右切角的空间,避免路径重叠。
- 阴影适配:添加
setLayerType(LAYER_TYPE_SOFTWARE, null),确保自定义Path的阴影能正常显示(硬件加速下自定义Path阴影可能失效)。
这样修改后,就能得到你想要的“左右圆形切角+四角完美圆角”的效果,不会出现变形或直线转角的问题。
内容的提问来源于stack exchange,提问作者Luther
相关产品推荐
相关产品推荐

