android 如何绘制圆角路径

2ledvvac  于 2023-03-27  发布在  Android
关注(0)|答案(3)|浏览(210)

我应该画一个像这样的自定义视图shot.

但是它们不一样。角的笔画是不同的。
我使用两个分开的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)

    }

更新:现在我画了这个,它的指针有两个边。

并使用此路径绘制:

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()
    }
}
bogh5gae

bogh5gae1#

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)
    }
}

顺便说一句,如果你想使用路径绘制圆角,你必须设置pathEffectCornerPathEffect
下面是一个例子:

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)
    }
}
mu0hgdu0

mu0hgdu02#

我想我有个办法

我花了很长时间才弄清楚那里发生了什么。
请注意,我的解决方案只绘制了一个圆角矩形,没有使用path,而是使用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的样式。这应该是很简单的。*

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

一开始我以为是角没有画好,我应该增加角的半径,但这不是一个解决方案。在做了一些研究和测试不同的实现后,我发现这个问题与角无关,而是视图本身。我的笔划只是被剪切,不能完全可见。
添加插图是解决我的问题的关键,而且做起来很简单。
这是修改后的代码:

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)
}

我只添加了一行,将插图设置为笔划宽度的一半,这是被剪切的确切大小。

inset(paint.strokeWidth / 2, paint.strokeWidth / 2)

我还对strokeWidth做了一些修改,使其看起来更漂亮(用2f代替4f)。
在那之后,瞧,你有中风,你会期望有。

希望我的解决方案能帮你保存时间和精力!

kxkpmulp

kxkpmulp3#

仅仅为了创建圆形路径,可以考虑使用

import com.google.android.material.shape.ShapeAppearanceModel
import com.google.android.material.shape.ShapeAppearancePathProvider

fun createRoundPath(width: Int, height: Int, roundSize: Float): Path {
    val roundPath = Path()
    val appearanceModel = ShapeAppearanceModel().withCornerSize(roundSize)
    val rect = RectF(0f, 0f, width.toFloat(), height.toFloat())
    ShapeAppearancePathProvider().calculatePath(appearanceModel, 1f, rect, roundPath)
    return roundPath
}

(fun事实上,我从其他地方的OP中学到了这一点,当时这个问题已经发布,我猜这并不存在)

相关问题