I'm trying to draw partial or one-side open rect rounded border to achieve this effect:
After playing around a bit I got this:
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)
) {}
}
}
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