Search code examples
drawingandroid-canvasandroid-jetpack-composestroke

Applying cap on only one end of a stroked line in Android


Is it possible to apply a cap to only one of the two ends of a stroke while drawing in a Compose Canvas DrawScope context?


Solution

  • There is no built-in solution.

    I could not find a solution for android.graphics.Canvas either: if you know how to do it in plain android canvas, you can use it in compose c with drawIntoCanvas.

    The only approach I can think of is to draw the caps on top:

    fun DrawScope.drawLine(
        color: Color,
        start: Offset,
        end: Offset,
        strokeWidth: Float = Stroke.HairlineWidth,
        startCap: StrokeCap,
        endCap: StrokeCap,
    ) {
        drawLine(
            color = color,
            start = start,
            end = end,
            strokeWidth = strokeWidth,
            cap = StrokeCap.Butt,
        )
        listOf(
            Triple(start, end, startCap),
            Triple(end, start, endCap),
        ).forEach {
            drawCap(
                color = color,
                start = it.first,
                end = it.second,
                strokeWidth = strokeWidth,
                cap = it.third
            )
        }
    }
    
    private fun DrawScope.drawCap(
        color: Color,
        start: Offset,
        end: Offset,
        strokeWidth: Float = Stroke.HairlineWidth,
        cap: StrokeCap,
    ) {
        when (cap) {
            StrokeCap.Butt -> Unit
            StrokeCap.Round -> {
                drawCircle(color, center = start, radius = strokeWidth / 2)
            }
            StrokeCap.Square -> {
                val offset = Offset(strokeWidth / 2, strokeWidth / 2)
                val size = Size(strokeWidth, strokeWidth)
    
                rotateRad(
                    radians = (end - start).run { atan2(x, y) },
                    pivot = start
                ) {
                    drawRect(color, topLeft = start - offset, size = size)
                }
            }
        }
    }
    

    Calculating angle in case for a line in StrokeCap.Square is pretty simple, but if you need this for curves the solution may not be that easy.

    But in you only need to support StrokeCap.Round knowing the start/end points and stroke width will be enough.