如何绘制带圆角的路径
How to draw a path with rounded corners
我应该像这个镜头一样画一个CustomView
但它们并不相同。边角笔触不同
我用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)
}
更新:
现在我画了这个,它的指针有两个面。
并使用此路径绘制:
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()
}
}
Canvas
有一些 pre-defined 绘制圆形和矩形等常见形状的方法。在您的场景中,您可以使用 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)
}
}
顺便说一句,如果要使用路径绘制圆角,必须将 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)
}
}
我想我有适合你的解决方案
我花了很长时间才弄清楚那里发生了什么。
Note that my solution draws only a rounded rectangle without using
path, but instead drawRoundRect
pre-defined method of Canvas
.
我正在创建一个自定义进度条,它会有 border/stroke。我首先想到的是创建一个 Paint
实例,将 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)
}
Note: The code above draws only the stroke, if you wanna draw the progress inside as well, you can create a new rect and set the style
of the paint Paint.Style.FILL
. It should be pretty straightforward
to do it.
嗯,这是我使用上面的代码绘制进度笔画时得到的结果,看起来不太好。
起初我认为角没有正确绘制,我应该增加角半径,但这不是解决方案。在做了一些研究和测试不同的实现之后,我发现这个问题与角落无关,而是与视图本身有关。我的笔画被剪掉了,还没有完全看出来。
添加 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)
I've also modified the strokeWidth
a bit for it to look nice (2f
instead of 4f
). You can
change it later to match your design requirements.
在那之后,瞧,你得到了你期望的中风。
希望我的解决方案能帮助您节省时间和精力!
我应该像这个镜头一样画一个CustomView
但它们并不相同。边角笔触不同
我用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)
}
更新:
现在我画了这个,它的指针有两个面。
并使用此路径绘制:
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()
}
}
Canvas
有一些 pre-defined 绘制圆形和矩形等常见形状的方法。在您的场景中,您可以使用 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)
}
}
顺便说一句,如果要使用路径绘制圆角,必须将 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)
}
}
我想我有适合你的解决方案
我花了很长时间才弄清楚那里发生了什么。
Note that my solution draws only a rounded rectangle without using path, but instead
drawRoundRect
pre-defined method ofCanvas
.
我正在创建一个自定义进度条,它会有 border/stroke。我首先想到的是创建一个 Paint
实例,将 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)
}
Note: The code above draws only the stroke, if you wanna draw the progress inside as well, you can create a new rect and set the style of the paint
Paint.Style.FILL
. It should be pretty straightforward to do it.
嗯,这是我使用上面的代码绘制进度笔画时得到的结果,看起来不太好。
起初我认为角没有正确绘制,我应该增加角半径,但这不是解决方案。在做了一些研究和测试不同的实现之后,我发现这个问题与角落无关,而是与视图本身有关。我的笔画被剪掉了,还没有完全看出来。
添加 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)
I've also modified the
strokeWidth
a bit for it to look nice (2f
instead of4f
). You can change it later to match your design requirements.
在那之后,瞧,你得到了你期望的中风。
希望我的解决方案能帮助您节省时间和精力!