如何在Jetpack Compose中实现文本自动滚动效果?
动态文本自动滚动实现方案
需求说明
从对象列表动态加载文本,文本长度存在差异:
- 短文本直接适配Text视图,保持静止
- 长文本超出视图范围时,需满足:
- 文本更新后延迟500毫秒开始滚动
- 以匀速向左滚动,直到显示最后一个字符后停止
- 下次文本更新时,自动回到初始位置,重复上述滚动逻辑
当前代码仅支持手动拖动滚动,原代码如下:
Column( modifier = Modifier .height(25.dp) .fillMaxSize() .background(color = slideCaptionBg, shape = RoundedCornerShape(16.dp)) .clickable { columnClicked.value = true }, horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { val textState = remember { Animatable(0f) } val scrollState = rememberScrollState() LaunchedEffect(Unit) { while (true) { val targetValue = if (textWidth < 800) (textWidth - 800).toFloat() else 0F textState.animateTo(targetValue, animationSpec = tween(800)) delay(500L) textState.animateTo(0F, animationSpec = tween(800)) delay(4000L) } } Row( modifier = Modifier .fillMaxSize() .padding(end = 8.dp, start = 8.dp) .width(IntrinsicSize.Min) .horizontalScroll(scrollState) .background(Color.Transparent), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = stringResource( R.string.song_art_slide_caption, // %s by %s <-- 带变量的字符串资源 newArtSlide.track, newArtSlide.artist ), modifier = Modifier .background(Color.Transparent), color = Color.White, fontSize = 16.sp, maxLines = 1, overflow = TextOverflow.Ellipsis, softWrap = false, style = MaterialTheme.typography.titleMedium, textAlign = TextAlign.Center, ) } if (columnClicked.value) { UtilFunctions.showSnackBar( coroutineScope = coroutineScope, snackState = snackState, message = "Show Song If Signed in!" ) columnClicked.value = false } }
修改后的实现代码
Column( modifier = Modifier .height(25.dp) .fillMaxSize() .background(color = slideCaptionBg, shape = RoundedCornerShape(16.dp)) .clickable { columnClicked.value = true }, horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { val scrollState = rememberScrollState() // 存储当前显示的文本,用于监听内容变化 val currentText = remember { mutableStateOf( stringResource( R.string.song_art_slide_caption, newArtSlide.track, newArtSlide.artist ) ) } // 用于测量文本实际渲染宽度 val textMeasurer = rememberTextMeasurer() // 存储容器可用宽度(减去左右padding后) val containerWidth = remember { mutableStateOf(0) } // 监听文本更新和容器宽度变化,触发滚动逻辑 LaunchedEffect(currentText.value, containerWidth.value) { // 每次文本更新,先重置滚动位置到初始状态 scrollState.scrollTo(0) // 计算文本实际宽度 val textLayoutResult = textMeasurer.measure( text = currentText.value, style = MaterialTheme.typography.titleMedium.copy( fontSize = 16.sp, color = Color.White ) ) val textWidth = textLayoutResult.size.width val availableWidth = containerWidth.value - 16.dp.toPx() // 减去左右各8dp的padding // 仅当文本宽度超过容器可用宽度时执行滚动 if (textWidth > availableWidth) { delay(500L) // 延迟500毫秒开始滚动 // 计算需要滚动的总距离 val scrollDistance = textWidth - availableWidth // 设置滚动速度:100dp/秒,转换为px/毫秒单位 val scrollSpeed = 100.dp.toPx() / 1000 // 根据滚动距离和速度计算时长,保证匀速滚动 val scrollDuration = (scrollDistance / scrollSpeed).toLong() // 平滑滚动到目标位置,匀速滚动保证可读性 scrollState.animateScrollTo( targetOffset = scrollDistance.toInt(), animationSpec = tween( durationMillis = scrollDuration.toInt(), easing = LinearEasing ) ) } } Row( modifier = Modifier .fillMaxSize() .padding(horizontal = 8.dp) // 监听容器尺寸变化,记录可用宽度 .onSizeChanged { size -> containerWidth.value = size.width } .horizontalScroll(scrollState) .background(Color.Transparent), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = currentText.value, modifier = Modifier.background(Color.Transparent), color = Color.White, fontSize = 16.sp, maxLines = 1, overflow = TextOverflow.Ellipsis, softWrap = false, style = MaterialTheme.typography.titleMedium, textAlign = TextAlign.Center, ) } if (columnClicked.value) { UtilFunctions.showSnackBar( coroutineScope = coroutineScope, snackState = snackState, message = "Show Song If Signed in!" ) columnClicked.value = false } }
关键修改点说明
- 准确判断滚动条件:通过
rememberTextMeasurer()测量文本实际宽度,结合onSizeChanged获取容器可用宽度,只有文本超出容器时才触发滚动 - 文本更新监听:用
LaunchedEffect监听文本内容变化,每次更新先重置滚动位置到初始状态 - 匀速滚动控制:根据滚动距离动态计算动画时长,搭配
LinearEasing实现匀速滚动,保证阅读舒适度 - 延迟触发逻辑:长文本更新后延迟500毫秒再开始滚动,符合需求要求
内容的提问来源于stack exchange,提问作者Klem Lloyd Mwenya
相关产品推荐
相关产品推荐

