Align Image with Text baseline in a Row
Asked Answered
P

2

7

How can I align an Image with a Text's baseline in a Row. Modifier.alignByBaseline() works for the Texts but the Image doesn't participate in the alignment.

@Composable
fun Sample() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center,
    ) {
        Text(
            text = "One",
            modifier = Modifier.alignByBaseline(),
            fontSize = 40.sp
        )
        Image(
            modifier = Modifier
                .padding(horizontal = 8.dp)
                .size(24.dp)
                .alignBy(FirstBaseline),
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "",
        )
        Text(
            text = "two",
            modifier = Modifier.alignByBaseline(),
            fontSize = 40.sp
        )
    }
}

enter image description here

Panslavism answered 25/8, 2021 at 5:20 Comment(0)
B
11

alignByBaseline aligns item by it's own baseline, not neighbours ones.

You can use paddingFromBaseline for Text and same value for image padding with verticalAlignment = Alignment.Bottom.

And to get actual value of baseline offset to pass it to the padding modifier, you need to wait for TextLayoutResult: it gets called when text layout is calculated depending on text size, font, etc.

Row(
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.Bottom,
    modifier = Modifier.fillMaxWidth()
) {
    var maxBaseline by remember { mutableStateOf(0f) }
    fun updateMaxBaseline(textLayoutResult: TextLayoutResult) {
        maxBaseline = max(maxBaseline, textLayoutResult.size.height - textLayoutResult.lastBaseline)
    }
    val topBaselinePadding = with(LocalDensity.current) { maxBaseline.toDp() }
    Text(
        text = "One",
        modifier = Modifier.paddingFromBaseline(bottom = topBaselinePadding),
        fontSize = 20.sp,
        onTextLayout = ::updateMaxBaseline
    )
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        contentDescription = "",
        modifier = Modifier
            .padding(bottom = topBaselinePadding)
            .size(24.dp)
    )
    Text(
        text = "two",
        modifier = Modifier.paddingFromBaseline(bottom = topBaselinePadding),
        fontSize = 40.sp,
        onTextLayout = ::updateMaxBaseline
    )
}

Berthaberthe answered 25/8, 2021 at 5:42 Comment(2)
Where does the 15.dp come from?Panslavism
@Panslavism ok actually that was a constant :D Only now the solution came to my mind how you can get real baseline value to pass it to these modifiers, check out updated answerBerthaberthe
C
0

The alignBy and alignByBaseline define own horizontal line, which will be used for alignment with other siblings.


We need to specify the line differently for images, and for texts:

  • alignBy { it.measuredHeight } for boxed elements, to align by bottom side of the box.

  • alignByBaseline for texts, to align by baseline.

See also this answer about alignBy.


Full example:

@Preview
@Composable
fun Sample() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center,
    ) {
        Text(
            text = "One",
            modifier = Modifier.alignByBaseline(),
            fontSize = 80.sp
        )
        Box(
            modifier = Modifier
                .padding(horizontal = 8.dp)
                .size(24.dp)
                .alignBy { it.measuredHeight }
                .background(color = Color.Blue)
        )
        Text(
            text = "two",
            modifier = Modifier.alignByBaseline(),
            fontSize = 40.sp
        )
        Icon(
            Icons.Default.CheckCircle,
            modifier = Modifier
                .padding(horizontal = 8.dp)
                .size(55.dp)
                .alignBy { it.measuredHeight }
                .background(color = Color.Red),
            contentDescription = "",
            tint = Color.Green,
        )
        Text(
            text = "Three",
            modifier = Modifier.alignByBaseline(),
            fontSize = 60.sp
        )
    }
}

bottom and baseline aligned images and texts

Carrnan answered 20/7 at 9:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.