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

Jetpack Compose实现垂直嵌套滚动及内部元素拖拽排序的方案咨询

Jetpack Compose 同方向嵌套滚动冲突解决方案

核心问题根源

你遇到的异常是Jetpack Compose的原生限制:

java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().
同方向的滚动容器嵌套会导致滚动手势歧义,官方明确禁止该用法。

可行解决方案

这里提供两种适配你业务场景的实现方式,都可以同时满足「页面整体滚动」+「组内动作拖拽重排」的需求:

方案1:单LazyColumn扁平化结构(性能最优)

将外层训练组卡片、组内训练动作都作为外层单个LazyColumn的独立条目,通过拖拽逻辑限制重排范围:

  • 提前对所有条目打标,标记每个条目属于哪个训练组、是组头还是动作项
  • 重写拖拽移动的判断逻辑,仅允许相同训练组内的动作项互相交换位置,跨组的拖拽操作直接拦截不生效
  • 整个页面只有一个滚动容器,完全规避嵌套滚动冲突,同时Lazy的复用能力可以支撑大量训练组/动作的场景

方案2:禁用内层LazyColumn滚动(改造成本最低)

不需要改动现有业务结构,仅修改内层LazyColumn的配置即可:

  • 给内层LazyColumn设置userScrollEnabled = false禁用自身滚动能力
  • 给内层LazyColumn添加Modifier.wrapContentHeight()让高度自适应内容,所有动作项直接全部展开
  • 外层LazyColumn负责整个页面的滚动,内层仅作为静态的动作列表容器,拖拽重排逻辑不受滚动禁用的影响,仍可正常完成组内重排

代码修改示例(基于方案2)

LazyColumn(Modifier.fillMaxSize()) {
    itemsIndexed(workout.workoutSets) { setIndex, workoutSet ->
        //State that contains the re-order drag & drop and calls the original list to be modified in my VM
        val state: ReorderableState = rememberReorderState(onMove = { from, to ->
            Grove.e { "Moving from $from to $to" }
            if (to <= workoutSet.exercises.lastIndex) {
                onExerciseMoved(setIndex, from, to)
            }
        })

        LazyColumn(
            state = state.listState,
            // 核心修改点:禁用自身滚动+高度自适应内容
            userScrollEnabled = false,
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentHeight()
                .reorderable(state) //Modifier that allows the drad&drop in this container
        ) {
            itemsIndexed(items = workoutSet.exercises, itemContent = { index, item ->
                Box(
                    Modifier
                        .fillMaxWidth()
                        .draggedItem(state.offset.takeIf { state.index == index }) //The dragged item modifier
                        .scale(if (state.index == null || state.index == index) 1f else .9f)
                ) {
                    CreatorExerciseItemRow(
                        onExerciseClick = { onExerciseClick(item) },
                        execution = item
                    )
                }
            })
        }
    }
}

注意事项

如果单个训练组内的动作数量超过20项,更推荐使用方案1,避免内层LazyColumn全量渲染带来的性能损耗。

内容的提问来源于stack exchange,提问作者Francisco Durdin Garcia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 12:51:04