You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose:实现LazyColumn滚动时文本动态缩放并固定

Jetpack Compose LazyColumn滚动时动态调整文本大小实现方案

需求是在Jetpack Compose的LazyColumn中实现滚动时文本动态调整大小,停止滚动后保持当前尺寸。具体规则为:中间选中的数字(如「4」)字号为20.sp,相邻的「3」「5」为18.sp,再外层的「2」「6」为16.sp,以此类推。尝试通过Modifier.scale()实现,但未找到具体方法,现有示例代码及依赖如下:

@OptIn(ExperimentalSnapperApi::class)
@Composable
fun Example() {

val listStateHour = rememberLazyListState()
val hourTexts: List<String> = (1..23).map { it.toString().padStart(2, '0') }

Box(
    modifier = Modifier.background(color = Color.LightGray).fillMaxWidth(),
    contentAlignment = Alignment.Center
) {
    Box(
        modifier = Modifier
            .height(32.dp)
            .fillMaxWidth()
            .background(color = Color.Black.copy(0.2f)),
    ) {}
    LazyColumn(
        modifier = Modifier.height(200.dp),
        state = listStateHour,
        horizontalAlignment = Alignment.Start,
        contentPadding = PaddingValues(vertical = 116.dp),
        flingBehavior = rememberSnapperFlingBehavior(lazyListState = listStateHour)
    ) {
        itemsIndexed(hourTexts) { index, item ->
            Box(
                modifier = Modifier.height(32.dp),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = item,
                    color = Color.Black,
                    fontSize = 20.sp
                )
            }
        }
    }
}
}

外部依赖:

implementation 'dev.chrisbanes.snapper:snapper:0.3.0'

解决方案

核心思路是利用LazyListState的滚动状态,实时计算每个列表项与中间参考区域的位置偏移量,根据偏移距离动态调整文本的缩放比例,从而实现字号变化的效果。具体实现如下:

@OptIn(ExperimentalSnapperApi::class)
@Composable
fun ScalingLazyColumnExample() {
    val listStateHour = rememberLazyListState()
    val hourTexts: List<String> = (1..23).map { it.toString().padStart(2, '0') }
    val itemHeight = 32.dp
    val itemHeightPx = with(LocalDensity.current) { itemHeight.toPx() }
    // LazyColumn自身的中心Y坐标
    val columnCenterY = 200.dp / 2

    // 实时计算每个可见item的缩放比例
    val itemScales by remember {
        derivedStateOf {
            val layoutInfo = listStateHour.layoutInfo
            val visibleItems = layoutInfo.visibleItemsInfo

            visibleItems.associate { item ->
                // 计算当前item的中心Y坐标(相对于LazyColumn的布局)
                val itemCenterY = item.offset + itemHeightPx / 2
                val columnCenterYPx = with(LocalDensity.current) { columnCenterY.toPx() }
                // 计算与中间位置的偏移item数量
                val offsetItems = abs((itemCenterY - columnCenterYPx) / itemHeightPx).toInt()
                // 根据偏移量匹配缩放比例,超出范围后保持最小缩放值
                val scale = when (offsetItems) {
                    0 -> 1f         // 中间项:20.sp * 1 = 20.sp
                    1 -> 0.9f       // 相邻项:20.sp * 0.9 = 18.sp
                    2 -> 0.8f       // 外层项:20.sp * 0.8 = 16.sp
                    else -> max(0.6f, 1f - offsetItems * 0.1f)
                }
                item.index to scale
            }
        }
    }

    Box(
        modifier = Modifier.background(color = Color.LightGray).fillMaxWidth(),
        contentAlignment = Alignment.Center
    ) {
        Box(
            modifier = Modifier
                .height(itemHeight)
                .fillMaxWidth()
                .background(color = Color.Black.copy(0.2f)),
        ) {}
        LazyColumn(
            modifier = Modifier.height(200.dp),
            state = listStateHour,
            horizontalAlignment = Alignment.CenterHorizontally,
            contentPadding = PaddingValues(vertical = 116.dp),
            flingBehavior = rememberSnapperFlingBehavior(lazyListState = listStateHour)
        ) {
            itemsIndexed(hourTexts) { index, item ->
                Box(
                    modifier = Modifier.height(itemHeight),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = item,
                        color = Color.Black,
                        fontSize = 20.sp,
                        // 应用计算好的缩放比例,不可见item使用默认最小缩放
                        modifier = Modifier.scale(itemScales.getOrDefault(index, 0.6f))
                    )
                }
            }
        }
    }
}

关键说明

  • 使用derivedStateOf监听布局变化,仅当可见item位置改变时重新计算缩放比例,避免不必要的重组,优化性能;
  • 通过item中心与LazyColumn中心的偏移量,转换为相对的item数量差,精准匹配需求中的字号规则;
  • 缩放比例与字号直接关联,基础字号20.sp通过scale系数动态调整,完全符合需求中的尺寸梯度;
  • 针对不可见item设置默认最小缩放值,避免首次加载或滚动到边缘时出现显示异常。

内容的提问来源于stack exchange,提问作者Svilen Rusev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:15:51