I am trying to draw a custom shape on a canvas that at present looks like this:

and
here is what I want to achieve:

Along with that, I want the attributes for the RGB borders to be configurable, for ex. should be able to change the stroke width as required. However, I am facing several issues:
- Not able to remove the base of the inverted triangle (The RGB border lines should not be straight at the bottom)
- If I try to change the width of the RGB lines (using paint.setStrokeWidth()), it introduces undesired gaps b/w them, whereas I want them to be continuous. I am sure I am making some calculation mistake, but can't figure it out.
- I have experienced that drawing a line along the view edge using lineTo on Path is drawn with half of the stroke width set on the Paint. However, I am unable to find out any reading material on the same. Can someone please enlighten me?
The onDraw method of the custom view is as below:
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()
}
Also, the onMeasure of the view class is as follows:
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()
)
}
I never found any official documentation on this, but I can confirm this happens not only with
Pathbut also withCanvas.drawRect()orCanvas.drawArc(), possibly becausePathis used under the hood (see for example Drawing an arc inside a circle). The behavior makes sense: why should aViewbe allowed to draw outside of its bounds? But I also wish we would not all have to learn by trial and error...The following screenshot shows two emulators with different specs side by side. One can see that the View is not always rendered the same way. The differences may be due to rounding - in the end you have to map a
Floatto anIntvalue, since the number of pixels on the device is anIntvalue.Since the
Paths are drawn one on top of the other, one approach could be to make the outer and main border wider, so that there is no gap.The base of the inverted triangle is part of the
Rectwhich you use for configuring thePath. You can fix that by using multiplelineTo()instead ofaddRect()Using the extension function in
onDraw():