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

如何让Row中的LazyColumn与自定义布局实现同步滚动?

同步LazyColumn与自定义布局的滚动状态

问题分析

你当前的代码仅实现了右侧自定义布局向LazyColumn的单向滚动同步,且ScheduleSidebar未接收外部传入的LazyListState,无法实现双向控制;同时LazyColumn的滚动偏移未换算为与右侧ScrollState匹配的单位,导致同步失效。

修复步骤

1. 修改ScheduleSidebar,支持外部控制滚动状态

让ScheduleSidebar接收外部的LazyListState,以便外部直接控制其滚动行为:

@Composable
fun ScheduleSidebar(
    hourHeight: Dp = 64.dp,
    listState: LazyListState // 新增外部状态参数
) {
    LazyColumn(
        state = listState, // 使用传入的状态替代默认状态
        modifier = Modifier
    ) {
        val startTime = LocalTime.MIN
        items(168) { hour ->
            Box(modifier = Modifier.height(hourHeight)) {
                Text(startTime.plusHours(hour.toLong()).toString())
            }
        }
    }
}

2. 实现双向滚动同步

通过两个LaunchedEffect分别监听两者的滚动变化,互相驱动,同时记录上一次滚动值避免循环触发:

@Composable
fun ScheduleView(
    viewModel: ScheduleViewModel = getViewModel()
) {
    val verticalScrollState = rememberScrollState()
    val lazyListState = rememberLazyListState()
    val hourHeightPx = 64.dp.toPx() // 提前转换为像素值,避免重复计算

    // 监听右侧自定义布局滚动,同步到LazyColumn
    LaunchedEffect(verticalScrollState, lazyListState) {
        var lastScrollValue = 0
        snapshotFlow { verticalScrollState.value }
            .collect { currentValue ->
                val delta = (currentValue - lastScrollValue).toFloat()
                lazyListState.scrollBy(delta)
                lastScrollValue = currentValue
            }
    }

    // 监听LazyColumn滚动,同步到右侧自定义布局
    LaunchedEffect(lazyListState, verticalScrollState, hourHeightPx) {
        var lastTotalOffset = 0f
        snapshotFlow {
            // 计算LazyColumn总滚动像素偏移:已滚动的完整item高度 + 当前item的偏移
            lazyListState.firstVisibleItemIndex * hourHeightPx + lazyListState.firstVisibleItemScrollOffset
        }
            .collect { currentOffset ->
                val delta = (currentOffset - lastTotalOffset).toInt()
                verticalScrollState.scrollTo(verticalScrollState.value + delta)
                lastTotalOffset = currentOffset
            }
    }

    Column(modifier = Modifier.fillMaxSize()) {
        ScheduleHeader()

        Row(modifier = Modifier.weight(1f)) {
            ScheduleSidebar(
                hourHeight = 64.dp,
                listState = lazyListState // 将状态传入Sidebar
            )

            Schedule(
                bookings = viewModel.state.value.bookingsList,
                modifier = Modifier.verticalScroll(verticalScrollState),
                onEmptyViewClicked = { days, hour, selectedDate ->
                    viewModel.onEvent(
                        ScheduleEvent.OnEmptyViewClicked(
                            days = days,
                            startTime = hour,
                            selectedDate = selectedDate
                        )
                    )
                }
            )
        }
    }
}

3. 核心要点

  • 双向同步:同时处理两种滚动触发场景,确保滚动任意一侧时,另一侧都能同步跟随
  • 滚动偏移换算:将LazyColumn的滚动偏移换算为与ScrollState一致的像素单位,通过firstVisibleItemIndex和firstVisibleItemScrollOffset计算总偏移量
  • 避免循环触发:每次滚动仅处理增量,通过记录上一次滚动值过滤重复触发逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:37:09