Issue
I had a BadgeView written with View using onMeasure, onLayout and OnDraw
I'm trying to migrate this View to Jetpack Compose.
Since drawing shapes is easier with compose i thought there is no need to use canvas or Layout
functions at all, but size of Text or Surface wrapping it is not set properly before text size is calculated, and circle is not drawn properly.
Also checked out Badge component, it uses static sizes BadgeWithContentRadius
, since in my design size depends on text size it's not possible to set a static size.
Surface(
shape = CircleShape,
contentColor = Color.White,
color = Color.Red
) {
Text(
text = "0",
modifier = Modifier.padding(4.dp),
fontSize = 34.sp,
)
}
Then tried using
var size: Dp by remember { mutableStateOf(40.dp) }
val density = LocalDensity.current
Surface(
shape = CircleShape,
modifier = Modifier.size(size),
contentColor = Color.Yellow,
color = Color.Red
){
Text(
text = "0",
modifier = Modifier.padding(4.dp),
fontSize = 24.sp,
onTextLayout = { textLayoutResult: TextLayoutResult ->
val textSize = textLayoutResult.size
val circleRadius = textSize.width.coerceAtLeast(textSize.height)
size = with(density) {
circleRadius.toDp()
}
println("Size: $size")
}
)
}
Both of the implementations are not working, then tried doing it with Layout
@Composable
private fun Badge(text: String, badgeState: BadgeState, modifier: Modifier = Modifier) {
Surface(shape = CircleShape, color = Color.Red, contentColor = Color.White) {
BadgeLayout(text = text, badgeState = badgeState, modifier = modifier)
}
}
@Composable
private fun BadgeLayout(text: String, badgeState: BadgeState, modifier: Modifier = Modifier) {
var circleRadius = 0
var size: IntSize by remember {
mutableStateOf(IntSize(0, 0))
}
val content = @Composable {
Text(
text = text,
modifier = Modifier.padding(4.dp),
fontSize = 34.sp,
onTextLayout = { textLayoutResult: TextLayoutResult ->
size = textLayoutResult.size
circleRadius = size.width.coerceAtLeast(size.height)
},
)
}
Layout(
modifier = modifier,
content = content
) { measurables: List<Measurable>, constraints: Constraints ->
val placeables = measurables.map { measurable ->
measurable.measure(constraints)
}
println("🔥 Badge: $circleRadius, size: $size")
layout(width = circleRadius, height = circleRadius) {
placeables.first().placeRelative(0, 0)
}
}
}
Shape seems to be applied correctly but couldn't find exact way to get text size to set number to center of Surface or Text.
How can a component, should have circle shape when it's one or digit number then turning it into RoundedCornerShape can be implemented with considering performance be implemented?
Solution
I've made the following modifier using Modifier.layout:
fun Modifier.badgeLayout() =
layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
// based on the expectation of only one line of text
val minPadding = placeable.height / 4
val width = maxOf(placeable.width + minPadding, placeable.height)
layout(width, placeable.height) {
placeable.place((width - placeable.width) / 2, 0)
}
}
Usage:
Text(
text,
modifier = Modifier
.background(MaterialTheme.colors.error, shape = CircleShape)
.badgeLayout()
)
Result:
Answered By - Philip Dukhov
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.