Home > Net >  Jetpack Compose partial or open side border
Jetpack Compose partial or open side border

Time:11-29

I'm trying to draw partial or one-side open rect rounded border to achieve this effect:

enter image description here

After playing around a bit I got this:

enter image description here

This is done via:

RoundedCornerShape(topStartPercent = 50, bottomStartPercent = 50) // start
RoundedCornerShape(topEndPercent = 50, bottomEndPercent = 50) // end
RectangleShape // for middle

What I want is to remove the connecting vertical lines between the cells.

CodePudding user response:

You can use the offset modifier to avoid the double border:

Something like:

val itemsList = (0..4).toList()

Row() {
    itemsList.forEachIndexed { index, item ->
        OutlinedButton(
            onClick = { /** do something */},
            modifier = when (index) {
                0 ->
                    Modifier
                        .offset(0.dp, 0.dp)
                else ->
                    Modifier
                        .offset((-1 * index).dp, 0.dp)
            },
            shape = when (index) {
                // left outer button
                0 -> RoundedCornerShape(topStart = cornerRadius, topEnd = 0.dp, bottomStart = cornerRadius, bottomEnd = 0.dp)
                // right outer button
                itemsList.size - 1 -> RoundedCornerShape(topStart = 0.dp, topEnd = cornerRadius, bottomStart = 0.dp, bottomEnd = cornerRadius)
                // middle button
                else -> RoundedCornerShape(0.dp)
            },
            border = BorderStroke(1.dp, Blue500)
        ) {}
    }
}

enter image description here

CodePudding user response:

You can draw border behind Composables using Modifier.drawBehind and checking if your Composable is at the start, center or end

enum class BorderOrder {
    Start, Center, End
}

fun Modifier.drawSegmentedBorder(
    strokeWidth: Dp,
    color: Color,
    cornerPercent: Int,
    borderOrder: BorderOrder,
    drawDivider: Boolean = false
) = composed(
    factory = {

        val density = LocalDensity.current
        val strokeWidthPx = density.run { strokeWidth.toPx() }

        Modifier.drawBehind {
            val width = size.width
            val height = size.height
            val cornerRadius = height * cornerPercent / 100

            when (borderOrder) {
                BorderOrder.Start -> {

                    drawLine(
                        color = color,
                        start = Offset(x = width, y = 0f),
                        end = Offset(x = cornerRadius, y = 0f),
                        strokeWidth = strokeWidthPx
                    )

                    // Top left arc
                    drawArc(
                        color = color,
                        startAngle = 180f,
                        sweepAngle = 90f,
                        useCenter = false,
                        topLeft = Offset.Zero,
                        size = Size(cornerRadius * 2, cornerRadius * 2),
                        style = Stroke(width = strokeWidthPx)
                    )
                    drawLine(
                        color = color,
                        start = Offset(x = 0f, y = cornerRadius),
                        end = Offset(x = 0f, y = height - cornerRadius),
                        strokeWidth = strokeWidthPx
                    )
                    // Bottom left arc
                    drawArc(
                        color = color,
                        startAngle = 90f,
                        sweepAngle = 90f,
                        useCenter = false,
                        topLeft = Offset(x = 0f, y = height - 2 * cornerRadius),
                        size = Size(cornerRadius * 2, cornerRadius * 2),
                        style = Stroke(width = strokeWidthPx)
                    )
                    drawLine(
                        color = color,
                        start = Offset(x = cornerRadius, y = height),
                        end = Offset(x = width, y = height),
                        strokeWidth = strokeWidthPx
                    )
                }
                BorderOrder.Center -> {
                    drawLine(
                        color = color,
                        start = Offset(x = 0f, y = 0f),
                        end = Offset(x = width, y = 0f),
                        strokeWidth = strokeWidthPx
                    )
                    drawLine(
                        color = color,
                        start = Offset(x = 0f, y = height),
                        end = Offset(x = width, y = height),
                        strokeWidth = strokeWidthPx
                    )

                    if (drawDivider) {
                        drawLine(
                            color = color,
                            start = Offset(x = 0f, y = 0f),
                            end = Offset(x = 0f, y = height),
                            strokeWidth = strokeWidthPx
                        )
                    }
                }
                else -> {

                    if (drawDivider) {
                        drawLine(
                            color = color,
                            start = Offset(x = 0f, y = 0f),
                            end = Offset(x = 0f, y = height),
                            strokeWidth = strokeWidthPx
                        )
                    }

                    drawLine(
                        color = color,
                        start = Offset(x = 0f, y = 0f),
                        end = Offset(x = width - cornerRadius, y = 0f),
                        strokeWidth = strokeWidthPx
                    )

                    // Top right arc
                    drawArc(
                        color = color,
                        startAngle = 270f,
                        sweepAngle = 90f,
                        useCenter = false,
                        topLeft = Offset(x = width - cornerRadius * 2, y = 0f),
                        size = Size(cornerRadius * 2, cornerRadius * 2),
                        style = Stroke(width = strokeWidthPx)
                    )
                    drawLine(
                        color = color,
                        start = Offset(x = width, y = cornerRadius),
                        end = Offset(x = width, y = height - cornerRadius),
                        strokeWidth = strokeWidthPx
                    )
                    // Bottom right arc
                    drawArc(
                        color = color,
                        startAngle = 0f,
                        sweepAngle = 90f,
                        useCenter = false,
                        topLeft = Offset(
                            x = width - 2 * cornerRadius,
                            y = height - 2 * cornerRadius
                        ),
                        size = Size(cornerRadius * 2, cornerRadius * 2),
                        style = Stroke(width = strokeWidthPx)
                    )
                    drawLine(
                        color = color,
                        start = Offset(x = 0f, y = height),
                        end = Offset(x = width -cornerRadius, y = height),
                        strokeWidth = strokeWidthPx
                    )
                }
            }
        }
    }
)

Usage

@Composable
private fun SegmentedBorderSample() {
    Row {
        repeat(3) {

            val order = when (it) {
                0 -> BorderOrder.Start
                2 -> BorderOrder.End
                else -> BorderOrder.Center
            }

            Box(
                contentAlignment = Alignment.Center,
                modifier = Modifier
                    .size(40.dp)
                    .drawSegmentedBorder(
                        strokeWidth = 2.dp,
                        color = Color.Green,
                        borderOrder = order,
                        cornerPercent = 40,
                        drawDivider = false
                    )
                    .padding(4.dp)
            ) {
                Text(text = "$it")
            }
        }
    }


    Row {
        repeat(4) {

            val order = when (it) {
                0 -> BorderOrder.Start
                3 -> BorderOrder.End
                else -> BorderOrder.Center
            }

            Box(
                contentAlignment = Alignment.Center,
                modifier = Modifier
                    .size(40.dp)
                    .drawSegmentedBorder(
                        strokeWidth = 2.dp,
                        color = Color.Cyan,
                        borderOrder = order,
                        cornerPercent = 50,
                        drawDivider = true
                    )
                    .padding(4.dp)
            ) {
                Text(text = "$it")
            }
        }
    }
}

Result

enter image description here

  • Related