1

我正在尝试在目前看起来像这样的画布上绘制自定义形状: 在此处输入图像描述

这就是我想要实现的目标: 在此处输入图像描述

除此之外,我希望 RGB 边框的属性是可配置的,例如。应该能够根据需要更改笔划宽度。但是,我面临几个问题:

  1. 倒三角的底边无法去除(RGB 边框线底部不应该是直的)
  2. 如果我尝试更改 RGB 线的宽度(使用 paint.setStrokeWidth()),它会在它们之间引入不希望的间隙,而我希望它们是连续的。我确定我犯了一些计算错误,但无法弄清楚。
  3. 我已经体验过使用 lineTo on Path 沿视图边缘绘制一条线是用 Paint 上设置的笔划宽度的一半绘制的。但是,我无法找到相同的任何阅读材料。有人可以启发我吗?

自定义视图的 onDraw 方法如下:

override fun onDraw(canvas: Canvas?) {
    outerBorderPath.reset()
    mainBorderPath.reset()
    innerBorderPath.reset()


    ///let's draw our content first
    canvas?.let { drawingCanvas ->
        outerBorderPath.addRect(outerBorderWidth.toFloat(),outerBorderWidth.toFloat(),width.toFloat() - outerBorderWidth, (height - arrowHeight - outerBorderWidth).toFloat(), Path.Direction.CW)
        outerBorderPath.addPath(mArrowPath, width.toFloat() - arrowWidth - 100,
            (height - arrowHeight - outerBorderWidth).toFloat()
        )
        drawingCanvas.drawPath(outerBorderPath, outerBorderPaint)
        mainBorderPath.addRect(outerBorderWidth + mainBorderWidth.toFloat(),
        outerBorderWidth + mainBorderWidth.toFloat(),
            width.toFloat() - outerBorderWidth - mainBorderWidth,
            (height - arrowHeight - outerBorderWidth - mainBorderWidth).toFloat(),
            Path.Direction.CW
            )

        mainBorderPath.addPath(mainArrowPath, width.toFloat() - arrowWidth + (outerBorderWidth/2) - 100,
            (height - arrowHeight - outerBorderWidth - mainBorderWidth).toFloat()
            )
        drawingCanvas.drawPath(mainBorderPath, mainBorderPaint)

        innerBorderPath.addRect(outerBorderWidth + mainBorderWidth + innerBorderWidth.toFloat(),
            outerBorderWidth + mainBorderWidth*1f + innerBorderWidth,
            width.toFloat() - outerBorderWidth - mainBorderWidth*1f - innerBorderWidth,
            (height - arrowHeight - outerBorderWidth - mainBorderWidth*1f - innerBorderWidth).toFloat(),
            Path.Direction.CW
            )
        innerBorderPath.addPath(innerArrowPath, width.toFloat() - arrowWidth + (outerBorderWidth + mainBorderWidth)/2 - 100,
            (height - arrowHeight - outerBorderWidth - mainBorderWidth - innerBorderWidth).toFloat()
            )
        drawingCanvas.drawPath(innerBorderPath, innerBorderPaint)
    }

    ///translate canvas to the child can be drawn now
    canvas?.save()
    super.onDraw(canvas)
    canvas?.restore()
}

另外,视图类的onMeasure如下:

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    setMeasuredDimension(
        (measuredWidth + (2 * outerBorderWidth) + (2 * innerBorderWidth) + (2 * innerPadding)).toInt(),
        (measuredHeight + (2 * outerBorderWidth) + (2 * innerBorderWidth) + (2 * innerPadding) + arrowHeight).toInt()
    )
}
4

1 回答 1

0

我已经体验过使用 lineTo on Path 沿视图边缘绘制一条线是用 Paint 上设置的笔划宽度的一半绘制的。但是,我无法找到相同的任何阅读材料。有人可以启发我吗?

我从来没有找到任何官方文档,但我可以确认这不仅发生在 with 上,Path而且还发生在Canvas.drawRect()orCanvas.drawArc()上,可能是因为Path它在幕后使用(例如,参见在圆内绘制圆弧)。这种行为是有道理的:为什么要View允许 a 超出其界限?但我也希望我们不必都通过反复试验来学习......

如果我尝试更改 RGB 线的宽度(使用 paint.setStrokeWidth()),它会在它们之间引入不希望的间隙,而我希望它们是连续的。我确定我犯了一些计算错误,但无法弄清楚。

以下屏幕截图并排显示了两个具有不同规格的模拟器。可以看到,视图并不总是以相同的方式呈现。差异可能是由于四舍五入 - 最后您必须将 a 映射Float到一个Int值,因为设备上的像素数是一个Int值。

由于Paths 是在另一个之上绘制的,一种方法可以是使外部和主边界更宽,以便没有间隙。

视图根据设备呈现不同

倒三角的底边无法去除(RGB 边框线底部不应该是直的)

倒三角形的底部是Rect您用于配置Path. 您可以通过使用多个lineTo()而不是addRect()

fun Path.createShapeWithPadding(padding: Int, arrowStartX: Int, arrowStartY:Int, width: Int, height: Int, arrowWidth: Int, arrowHeight: Int ) {

    val paddingF = padding.toFloat()
    moveTo(paddingF, paddingF)
    lineTo(width - paddingF, paddingF)
    lineTo(width - paddingF, arrowStartY.toFloat())
    lineTo(arrowStartX.toFloat(), arrowStartY.toFloat())
    rLineTo(-arrowWidth / 2f, arrowHeight.toFloat())
    rLineTo(-arrowWidth / 2f, -arrowHeight.toFloat())
    lineTo(paddingF, arrowStartY.toFloat())
    close()
}

使用扩展功能onDraw()

override fun onDraw(canvas: Canvas?) {
    outerBorderPath.reset()
    mainBorderPath.reset()
    innerBorderPath.reset()
    
    canvas?.let { drawingCanvas ->

        val arrowX = width - 100
        val arrowY = height - arrowHeight - outerBorderWidth
        outerBorderPath.apply {
            createShapeWithPadding(outerBorderWidth, arrowX, arrowY, width, height, arrowWidth, arrowHeight)
        }

        drawingCanvas.drawPath(outerBorderPath, outerBorderPaint)

        mainBorderPath.apply {
            createShapeWithPadding(
                mainBorderWidth + outerBorderWidth,
                arrowX - (outerBorderWidth / 2) ,
                arrowY - mainBorderWidth,
                width,
                height,
                arrowWidth - outerBorderWidth,
                arrowHeight - outerBorderWidth
            )
        }
        drawingCanvas.drawPath(mainBorderPath, mainBorderPaint)

        innerBorderPath.apply {
            createShapeWithPadding(
                outerBorderWidth + mainBorderWidth + innerBorderWidth,
                arrowX - (outerBorderWidth + mainBorderWidth) / 2,
                arrowY - (mainBorderWidth + innerBorderWidth),
                width,
                height,
                arrowWidth - (outerBorderWidth + mainBorderWidth),
                arrowHeight - (outerBorderWidth + mainBorderWidth)
            )
        }
        drawingCanvas.drawPath(innerBorderPath, innerBorderPaint)

    }

    canvas?.save()
    super.onDraw(canvas)
    canvas?.restore()
}
于 2021-12-16T10:27:58.673 回答