如何在Jetpack Compose中实现类似WhatsApp的文本与时间排版
在Jetpack Compose中实现WhatsApp风格的文本+时间排版
要实现类似WhatsApp的消息排版逻辑:短文本时时间与消息同行居右,长文本或换行文本时时间自动对齐到消息块的右下角(可在最后一行末尾或单独一行),可以通过以下两种方式实现:
方法一:使用Accompanist FlowRow(简单高效)
Accompanist库的FlowRow组件支持自动换行,能轻松实现这种流式排版效果。
步骤:
- 添加Accompanist FlowLayout依赖到
build.gradle:
dependencies { implementation "com.google.accompanist:accompanist-flowlayout:<latest-version>" }
(替换<latest-version>为当前最新版本,如0.35.0-alpha)
- 自定义组合函数:
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.MainAxisAlignment import com.google.accompanist.flowlayout.CrossAxisAlignment @Composable fun WhatsAppStyledMessage( message: String, time: String, modifier: Modifier = Modifier, messageTextStyle: TextStyle = TextStyle.Default, timeTextStyle: TextStyle = TextStyle.Default.copy(color = Color.Gray) ) { FlowRow( modifier = modifier.fillMaxWidth(), mainAxisAlignment = MainAxisAlignment.SpaceBetween, crossAxisAlignment = CrossAxisAlignment.End, mainAxisSize = com.google.accompanist.flowlayout.SizeMode.Wrap ) { Text( text = message, style = messageTextStyle, modifier = Modifier.align(CrossAxisAlignment.Start) ) Text( text = time, style = timeTextStyle, modifier = Modifier.padding(start = 8.dp) ) } }
效果说明:
- 短文本场景:时间直接跟在消息右侧,与消息同行显示。
- 长文本场景:当最后一行无法容纳时间时,时间自动换行并对齐到消息块右下角。
- 换行文本场景:消息换行后最后一行仍有空间时,时间直接放在最后一行末尾。
方法二:自定义Layout组件(无额外依赖)
如果不想引入第三方库,可以通过自定义Layout组件完全控制测量和布局逻辑:
代码实现:
import androidx.compose.foundation.layout.Layout import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Constraints @Composable fun MessageWithTime( message: String, time: String, modifier: Modifier = Modifier, messageTextStyle: TextStyle = TextStyle.Default, timeTextStyle: TextStyle = TextStyle.Default.copy(color = Color.Gray) ) { Layout( modifier = modifier, content = { Text(text = message, style = messageTextStyle) Text(text = time, style = timeTextStyle) } ) { measurables, constraints -> val messageMeasurable = measurables[0] val timeMeasurable = measurables[1] // 测量时间文本尺寸 val timePlaceable = timeMeasurable.measure(constraints.copy(minWidth = 0)) val timeWidth = timePlaceable.width val timeHeight = timePlaceable.height // 测量消息文本在全宽下的行数 val fullWidthMessage = messageMeasurable.measure(constraints) val fullLineCount = fullWidthMessage.lineCount // 测量消息文本减去时间宽度后的行数,判断是否需要换行放时间 val reducedWidthConstraints = constraints.copy(maxWidth = constraints.maxWidth - timeWidth) val reducedWidthMessage = messageMeasurable.measure(reducedWidthConstraints) val reducedLineCount = reducedWidthMessage.lineCount val placeTimeOnNewLine = reducedLineCount > fullLineCount val finalMessagePlaceable = if (placeTimeOnNewLine) fullWidthMessage else reducedWidthMessage // 计算整体布局宽高 val totalWidth = maxOf( if (placeTimeOnNewLine) constraints.maxWidth else finalMessagePlaceable.width + timeWidth, timeWidth ) val totalHeight = if (placeTimeOnNewLine) finalMessagePlaceable.height + timeHeight else maxOf(finalMessagePlaceable.height, timeHeight) // 执行布局 layout(totalWidth, totalHeight) { if (placeTimeOnNewLine) { finalMessagePlaceable.placeRelative(0, 0) timePlaceable.placeRelative(totalWidth - timeWidth, finalMessagePlaceable.height) } else { finalMessagePlaceable.placeRelative(0, 0) // 单行消息垂直居中时间,多行消息对齐消息底部 val timeY = if (fullLineCount == 1) { (finalMessagePlaceable.height - timeHeight) / 2 } else { finalMessagePlaceable.height - timeHeight } timePlaceable.placeRelative(finalMessagePlaceable.width, timeY) } } } }
使用示例:
@Composable fun DemoMessages() { Column(modifier = Modifier.padding(16.dp)) { WhatsAppStyledMessage( message = "lorem ipsum dolor", time = "12:34 PM" ) Spacer(modifier = Modifier.height(8.dp)) WhatsAppStyledMessage( message = "lorem ipsum dolor....foobar", time = "12:30 PM" ) Spacer(modifier = Modifier.height(8.dp)) WhatsAppStyledMessage( message = "lorem ipsum dolor sit amet\nlorem ipsum tai bau", time = "12:30 PM" ) } }
内容的提问来源于stack exchange,提问作者Umar
相关产品推荐
相关产品推荐

