Compose - 使用 Reorderable

一、概念

支持列表(Column&Row)、滑动列表(LazyList)、网格(LazyGrid)、瀑布流(LazyStaggeredGrid)。

  • **不同尺寸元素支持:**列表中可以包含大小不一的元素。
  • 可选不可重排项:部分元素可以设置为不响应拖放操作。
  • **多种启动方式:**支持直接拖放和长按启动两种模式。
  • **边缘自动滚动:**拖拽到屏幕边缘时自动滚动列表。
  • **流畅动画效果:**利用 Modifier.animateItem() 实现平滑过渡动画。
  • **拖拽句柄支持:**可以使用子组件作为拖拽句柄,即使设置条目中触发拖动的位置。
  • **触觉反馈集成:**支持拖拽过程中的震动反馈。

|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ReorderableItem() | @Composable fun LazyStaggeredGridItemScope.ReorderableItem( state: ReorderableLazyStaggeredGridState, key: Any, modifier: Modifier = Modifier, enabled: Boolean = true, animateItemModifier: Modifier = Modifier.animateItem(), content: @Composable ReorderableCollectionItemScope.(isDragging: Boolean) -> Unit, ) 包裹可拖动的条目。 |
| draggableHandle() | fun Modifier.draggableHandle( enabled: Boolean = true, interactionSource: MutableInteractionSource? = null, onDragStarted: (startedPosition: Offset) -> Unit = {}, onDragStopped: () -> Unit = {}, dragGestureDetector: DragGestureDetector = DragGestureDetector.Press ): Modifier 设置给条目中触发拖动的位置(拖动范围)。 |
| longPressDraggableHandle() | fun Modifier.longPressDraggableHandle( enabled: Boolean = true, interactionSource: MutableInteractionSource? = null, onDragStarted: (startedPosition: Offset) -> Unit = {}, onDragStopped: () -> Unit = {}, ): Modifier 长按触发拖动。 |

二、添加依赖

最新版本

Kotlin 复制代码
[versions]
reorderable = "3.0.0"

[libraries]
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }

三、简单使用

Kotlin 复制代码
val lazyListState = rememberLazyListState()
val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to ->
    //重排后更新列表
    list = list.toMutableList().apply {
        add(to.index, removeAt(from.index))
    }
}

LazyColumn(state = lazyListState) {
    items(list, key = { it.id }) {
        //包裹一下条目内容
        ReorderableItem(reorderableLazyListState, key = it.id) { isDragging ->
            IconButton(
                //设置条目中触发拖动的位置
                modifier = Modifier.draggableHandle()
            )
        }
    }
}

四、进阶使用

4.1 固定不可拖拽的条目

有时需要某些项目保持固定位置,比如标题栏或分隔符。

onMove 中的 from.index 和 to.index 表示项目在 LazyColumn 中的原始索引。若使用了分组头部或尾部,可能需要相应调整这些索引值。

XML 复制代码
val reorderableLazyColumnState = rememberReorderableLazyListState(lazyListState) { from, to ->
    list = list.toMutableList().apply {
        add(to.index - 1, removeAt(from.index - 1))
    }
}
Kotlin 复制代码
items(list) { index, item ->
    if (...) {
        // 渲染固定项目
    } else {
        ReorderableItem(state, key = item.id) {
            // 渲染可拖拽项目
        }
    }
}

4.2 添加震动反馈

XML 复制代码
<uses-permission android:name="android.permission.VIBRATE" />
Kotlin 复制代码
var list by remember { mutableStateOf(List(100) { "Item $it" }) }
val hapticFeedback = LocalHapticFeedback.current
val lazyListState = rememberLazyListState()
val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to ->
    list = list.toMutableList().apply {
        add(to.index, removeAt(from.index))
    }
    hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick)
}

LazyColumn(
    modifier = Modifier.fillMaxSize(),
    state = lazyListState,
    contentPadding = PaddingValues(8.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp),
) {
    items(list, key = { it }) {
        ReorderableItem(reorderableLazyListState, key = it) { isDragging ->
            val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp)
            Surface(shadowElevation = elevation) {
                Row {
                    Text(it, Modifier.padding(horizontal = 8.dp))
                    IconButton(
                        modifier = Modifier.draggableHandle(
                            onDragStarted = {
                                hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate)
                            },
                            onDragStopped = {
                                hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd)
                            },
                        ),
                        onClick = {},
                    ) {
                        Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
                    }
                }
            }
        }
    }
}

4.3 传递 ReorderableCollectionItemScope 给子组件

Modifier.draggableHandle 和 Modifier.longPressDraggableHandle 仅能在 ReorderableCollectionItemScope 作用域内使用,因此需要传递给子组件。

Kotlin 复制代码
LazyColumn(state = lazyListState) {
    items(list, key = { /* item key */ }) {
        ReorderableItem(reorderableLazyListState, key = /* item key */) { isDragging ->
            MyItem(this)
        }
    }
}

@Composable
fun MyItem(scope: ReorderableCollectionItemScope) {
    IconButton(
        modifier = with(scope) {
            Modifier.draggableHandle()
        }
    )
}

4.4 避开系统栏区域

以下是库作者的解决方案,推荐 Modifier.safeContentPadding() 设置给屏幕级组件更方便,详见

若您的 LazyColumn 在导航栏或状态栏下方显示,可考虑为 rememberReorderableLazyListState 添加 scrollThresholdPadding 参数,将滚动触发区域移出导航栏或状态栏的遮挡范围。

Kotlin 复制代码
val reorderableLazyListState = rememberReorderableLazyListState(
    lazyListState = lazyListState,
    scrollThresholdPadding = WindowInsets.systemBars.asPaddingValues(),
) { from, to ->
    ...
}
相关推荐
SharpCJ6 小时前
Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革
android·ai·aigc
_李小白7 小时前
【OSG学习笔记】Day 38: TextureVisitor(纹理访问器)
android·笔记·学习
JJay.7 小时前
Kotlin 高阶函数学习指南
android·开发语言·kotlin
jinanwuhuaguo7 小时前
截止到4月8日,OpenClaw 2026年4月更新深度解读剖析:从“能力回归”到“信任内建”的范式跃迁
android·开发语言·人工智能·深度学习·kotlin
JJay.7 小时前
Android Kotlin 协程使用指南
android·开发语言·kotlin
BLUcoding8 小时前
Android 布局介绍
android
summerkissyou19878 小时前
android-蓝牙-状态和协议值总结及监听例子
android·蓝牙
徒 花8 小时前
数据库知识复习05
android·数据库
提子拌饭13310 小时前
番茄时间管理:鸿蒙Flutter 实现的高效时间管理工具
android·flutter·华为·架构·开源·harmonyos·鸿蒙
4311媒体网11 小时前
帝国CMS二次开发实战:精准实现“最新资讯”标识与高亮判断
android