如何绘制带有圆角的路径

时间:2019-05-25 21:13:36

标签: android android-canvas android-custom-view custom-view

我应该画出这样的CustomView。enter image description here

但它们不相同。角落的笔触是不同的。

我使用2个分开的Path绘制顶部形状: 第一个用于黄色背景:

   private val paint = Paint().apply {
        isAntiAlias = false                    // pass true does not make change
        color = Color.YELLOW
        style = Paint.Style.FILL_AND_STROKE    // pass only FILL does not make change
        }

第二个是:

private val strokePaint = Paint().apply {
        isAntiAlias = false                   // pass true does not make change
        color = Color.BLACK
        strokeWidth = 2.toPx().toFloat()
        style = Paint.Style.STROKE
    }

onDraw()函数中,我用它们绘制:

override fun onDraw(canvas: Canvas) {
        drawPath()

        canvas.drawPath(path, paint)
        canvas.drawPath(path, strokePaint)

        // at the end, draw text and default things to avoid overlapping with background
        super.onDraw(canvas)

    }

更新: 现在我画了一个有两个侧面的指针。 enter image description here

并使用此路径绘制:

 private fun drawPath() {
    path.run {
        moveTo(left + radius, top)

        if (_side == SIDE_TOP) {
            lineTo(pointerX - pointerSize / 2, top)
            lineTo(pointerX, rect.top)
            lineTo(pointerX + pointerSize / 2, top)
        }
        lineTo(right - radius, top)

        arcTo(topRightRect, 270F, 90F, false)
        lineTo(right, bottom - radius)
        arcTo(bottomRightRect, 0F, 90F, false)

        if (_side == SIDE_BOTTOM) {
            lineTo(pointerX + pointerSize / 2, bottom)
            lineTo(pointerX, rect.bottom)
            lineTo(pointerX - pointerSize / 2, bottom)
        }
        lineTo(left + radius, bottom)

        arcTo(bottomLeftRect, 90F, 90F, false)
        lineTo(left, top + radius)
        arcTo(topLeftRect, 180F, 90F, false)
        close()
    }
}

2 个答案:

答案 0 :(得分:1)

Canvas具有一些用于绘制常见形状(如圆形和矩形)的预定义方法。在您的方案中,您可以使用drawRoundRect来绘制矩形,而该RectF需要使用class RoundedRect @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val roundCorner = 32f private val paint = Paint().apply { color = Color.YELLOW style = Paint.Style.FILL isAntiAlias = true } private val strokePaint = Paint().apply { color = Color.BLACK strokeWidth = 4f style = Paint.Style.STROKE isAntiAlias = true } private var rect = RectF(0f, 0f, 0f, 0f) override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) rect = RectF(0f, 0f, w.toFloat(), h.toFloat()) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRoundRect(rect, roundCorner, roundCorner, paint) canvas.drawRoundRect(rect, roundCorner, roundCorner, strokePaint) } }

这里是一个例子:

enter image description here

pathEffect

顺便说一句,如果要使用路径绘制圆角,则必须将CornerPathEffect设置为 class RoundedRectUsingPath @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val roundCorner = 32f private val paint = Paint().apply { color = Color.YELLOW isAntiAlias = true pathEffect = CornerPathEffect(roundCorner) strokeCap = Paint.Cap.ROUND } private val strokePaint = Paint().apply { color = Color.BLACK strokeWidth = 4f isAntiAlias = true style = Paint.Style.STROKE pathEffect = CornerPathEffect(roundCorner) } private var path = Path() private val offset = 50f override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) path = Path().apply { moveTo(offset, offset) lineTo(w.toFloat() - offset, offset) lineTo(w.toFloat() - offset, h.toFloat() - offset) lineTo(offset, h.toFloat() - offset) } path.close() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawPath(path, paint) canvas.drawPath(path, strokePaint) } }

这里是一个示例: enter image description here

user: FirebaseUser({uid: hGLEDW6OT5ZMhWra9L4p6bB4Pw92, isAnonymous: false, phoneNumber: +79644054946, providerData: [{uid: hGLEDW6OT5ZMhWra9L4p6bB4Pw92, phoneNumber: +79644054946, providerId: firebase}], providerId: firebase, creationTimestamp: 1557420327980, lastSignInTimestamp: 1558848790729, isEmailVerified: false} 

答案 1 :(得分:1)

我想我为你找到了解决方案

我花了很长时间才弄清楚那里发生了什么。

<块引用>

请注意,我的解决方案只绘制了一个圆角矩形而不使用 路径,而是 drawRoundRect 的预定义方法 Canvas

我正在创建一个带有边框/笔划的自定义进度条。我想到的第一件事是创建一个 Paint 实例,将绘画样式设置为 Paint.Style.STROKE,然后绘制一个圆角矩形。

override fun dispatchDraw(canvas: Canvas?) {
    // I do the actual initialisations outside of dispatchDraw(). This is just an example.
    val paint: Paint = Paint()
    val strokeWidth = 4f
    val cornerRadius = 10f
    val strokeColor = Color.RED
    val strokeRect = RectF().apply {
        set(0f, 0f, width.toFloat(), height.toFloat())
    }
    
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = strokeWidth
    paint.color = strokeColor
    canvas?.drawRoundRect(strokeRect, cornerRadius, cornerRadius, paint)
}
<块引用>

注意:上面的代码只绘制了笔划,如果你也想在里面绘制进度,你可以新建一个矩形并设置样式 油漆 Paint.Style.FILL。应该很简单 去做。

好吧,这就是我使用上面的代码绘制进度笔划时得到的结果,看起来不太好。

Initial version of the progress bar with a stroke

首先我认为角没有正确绘制,我可能应该增加角半径,但这不是解决方案。在做了一些研究和测试不同的实现之后,我发现问题与角落无关,而是与视图本身有关。我的笔触被剪掉了,不完全可见。

添加 insets 是解决我的问题的关键,而且操作起来非常简单。

这是修改后的代码:

override fun dispatchDraw(canvas: Canvas?) {
    // I do the actual initialisations outside of dispatchDraw(). This is just an example.
    val paint: Paint = Paint()
    val strokeWidth = 2f
    val cornerRadius = 10f
    val strokeColor = Color.RED
    val strokeRect = RectF().apply {
        set(0f, 0f, width.toFloat(), height.toFloat())
        inset(paint.strokeWidth / 2, paint.strokeWidth / 2)
    }
    
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = strokeWidth
    paint.color = strokeColor
    canvas?.drawRoundRect(strokeRect, cornerRadius, cornerRadius, paint)
}

我只添加了一行,将 insets 设置为笔划宽度的一半,这是被剪切的确切尺寸。

inset(paint.strokeWidth / 2, paint.strokeWidth / 2)
<块引用>

我还稍微修改了 strokeWidth 以使其看起来不错(2f 而不是 4f)。你可以 稍后更改以符合您的设计要求。

在那之后,你就会有你期望的中风。

Final version of the progress bar with a stroke

希望我的解决方案能帮助您节省时间和精力!