【Jetpack Compose】LazyColumn 拖动排序

2,427 阅读1分钟

Compose版本1.2.1 ,目前官方的Jetpack Compose 暂时还不支持LazyList的拖动排序,这里我们使用第三方开源库ComposeReorderable

dependencies {
    implementation("org.burnoutcrew.composereorderable:reorderable:<latest_version>")
}

这里我们用最新的版本0.9.2

1.长按触发拖动排序

这里就直接用ComposeReorderable官方的栗子就好了,用的是modifier.detectReorderAfterLongPress(state)

@Composable
fun VerticalReorderList() {
    val data = remember { mutableStateOf(List(100) { "Item $it" }) }
    val state = rememberReorderableLazyListState(onMove = { from, to ->
        data.value = data.value.toMutableList().apply {
            add(to.index, removeAt(from.index))
        }
    })
    LazyColumn(
        state = state.listState,
        modifier = Modifier
        .reorderable(state)
        .detectReorderAfterLongPress(state)
    ) {
        items(data.value, { it }) { item ->
            ReorderableItem(state, key = item) { isDragging ->
                val elevation = animateDpAsState(if (isDragging) 16.dp else 0.dp)
                Column(
                    modifier = Modifier
                        .shadow(elevation.value)
                        .background(MaterialTheme.colors.surface)
                ) {
                    Text(item,modifier = Modifier
                            .fillMaxWidth()
                            .height(45.dp))
                }
            }
        }
    }
}

2.拖动右侧Icon进行拖动排序,如顶图

用modifier = Modifier.detectReorder(state)

@Composable
fun DragListDemo() {
    Scaffold(topBar = { CenterTopAppBar(title = "拖动排序") }) { paddingValues ->
        Column(Modifier.padding(paddingValues)) {

            val data =
                remember { mutableStateOf(List(100) { "Item $it" }) }

            val state = rememberReorderableLazyListState(onMove = { from, to ->
                data.value = data.value.toMutableList().apply {
                    add(to.index, removeAt(from.index))
                }
            })


            LazyColumn(
                state = state.listState,
                modifier = Modifier
                    .reorderable(state)
            ) {
                itemsIndexed(items = data.value, key = { _, it -> it }) { index, item ->
                    ReorderableItem(state, key = item) { isDragging ->
                        val elevation = animateDpAsState(if (isDragging) 16.dp else 0.dp)
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(45.dp)
                                .shadow(elevation.value)
                                .background(MaterialTheme.colors.surface)
                                .padding(horizontal = 15.dp),
                            horizontalArrangement = Arrangement.SpaceBetween,
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Text(item)

                            Icon(
                                imageVector = Icons.Rounded.Menu,
                                contentDescription = "",
                                modifier = Modifier.detectReorder(state)
                            )
                        }
                    }
                }
            }
        }
    }
}

3. 当list超过一页,拖动一下item,LazyColumn就会自动滑动到最后一页的问题。

记录一下我碰到的这问题, 为了让这个dargList compose 可以重用,就简单的把它拆分了一下:

@Composable
fun DragListDemo() {
    Scaffold(topBar = { CenterTopAppBar(title = "拖动排序") }) { paddingValues ->
        Column(Modifier.padding(paddingValues)) {

            val data =
                remember { mutableStateOf(List(100) { "Item $it" }) }

            val state = rememberReorderableLazyListState(onMove = { from, to ->
                data.value = data.value.toMutableList().apply {
                    add(to.index, removeAt(from.index))
                }
            })

            DragListContent(state,data.value)

        }
    }
}

dragList 和数据源解耦

@Composable
fun DragListContent(state: ReorderableLazyListState, list: List<String>) {
    LazyColumn(
        state = state.listState,
        modifier = Modifier
            .reorderable(state)
    ) {
        items(items = list, key = {  it }) { item ->
            ReorderableItem(state, key = item) { isDragging ->
                val elevation = animateDpAsState(if (isDragging) 16.dp else 0.dp)
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(45.dp)
                        .shadow(elevation.value)
                        .background(MaterialTheme.colors.surface)
                        .padding(horizontal = 15.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(item)

                    Icon(
                        imageVector = Icons.Rounded.Menu,
                        contentDescription = "",
                        modifier = Modifier.detectReorder(state)
                    )
                }
            }
        }
    }
}

然后运行,结果如图:

ehhrx-3gxzb.gif

可以看到,item只拖动了一点点距离,lazyColumn就自动滑动到最后一页了。

然后又把它还原回去,把lazyColumn和数据放一起,结果就好了没有这个问题了。

那一定要把数据和lazyColumn拆开怎么办:

解决方案一

用State<List> 做参数,因为State是稳定类型的,而List不是稳定类型的。

@Composable
fun DragListContent(state: ReorderableLazyListState, list: State<List<String>>) {
    LazyColumn(
        state = state.listState,
        modifier = Modifier
            .reorderable(state)
    ) {
        items(items = list.value, key = {  it }) { item ->
            ReorderableItem(state, key = item) { isDragging ->
                val elevation = animateDpAsState(if (isDragging) 16.dp else 0.dp)
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(45.dp)
                        .shadow(elevation.value)
                        .background(MaterialTheme.colors.surface)
                        .padding(horizontal = 15.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(item)
                    Icon(
                        imageVector = Icons.Rounded.Menu,
                        contentDescription = "",
                        modifier = Modifier.detectReorder(state)
                    )
                }
            }
        }
    }
}

那不用State一定要用list做参数呢

解决方案二

用SnapshotStateList

@Composable
fun DragListDemo() {
    Scaffold(topBar = { CenterTopAppBar(title = "拖动排序") }) { paddingValues ->
        Column(Modifier.padding(paddingValues)) {

            val data =
                remember { List(100) { "Item $it" }.toMutableStateList() }

            val state = rememberReorderableLazyListState(onMove = { from, to ->
                data.apply {
                    add(to.index, removeAt(from.index))
                }
            })

            DragListContent(state,data)

        }
    }
}