I have the following jetpack compose layout (I added .border
so that the view bounds are visible):
@Composable
fun ExpandableCard(
expanded: Boolean
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(Dimens.commonMdSpacing),
horizontalArrangement = Arrangement.Start
) {
Icon(
imageVector = if (expanded)
Icons.Default.RemoveCircleOutline
else
Icons.Default.AddCircleOutline,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.size(22.dp)
.border(1.dp, Color.Black)
)
Text(
text = "Some really really really really really long title",
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
modifier = Modifier
.padding(start = 16.dp)
.border(1.dp, Color.Black)
)
}
}
I want the Icon
to be aligned to the first line of the Text
. This looks good when the font scale of the device is 1 (the bottom preview "Standard").
But the upper preview is a font scale of 2.5x and the icon is no longer aligned to the first line of text. Can this be achieved? preferably without complex view measurements
There is way which involves indirect view measurement. Text composable provide a onTextLayout callback which gives the information about line top and line bottom, also there is information about first line's baseline, I have not used it in this solution but you can give it a try as well.
Also if you need to use border then count the border height as well
@Composable
fun ExpandableCard(
expanded: Boolean
) {
val iconSize = 22f
Row(
modifier = Modifier
.fillMaxWidth().wrapContentHeight()
.padding(12.dp),
horizontalArrangement = Arrangement.Start
) {
var topPadding by remember {
mutableStateOf(0f)
}
Box(modifier = Modifier.padding(top = 0f.coerceAtLeast(topPadding.toDp(LocalContext.current) - iconSize/2).dp)) {
Icon(
imageVector = if (expanded)
Icons.Default.Refresh
else
Icons.Default.Add,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.size(iconSize.dp)
.border(1.dp, Color.Black)
)
}
Text (
text = "Some really really really really really long title",
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
modifier = Modifier
.padding(start = 16.dp)
.border(1.dp, Color.Black),
onTextLayout = {
topPadding = (it.getLineTop(0) + it.getLineBottom(0))/2
}
)
}
}
fun Float.toDp(context: Context): Float = this / context.resources.displayMetrics.density