LoginSignup
15
4

More than 1 year has passed since last update.

【Android】 Jetpack Composeでドラッグ&ドロップの並び替えを実現する 〜ライブラリに頼って〜

Last updated at Posted at 2021-12-04

この記事は、and factory.inc Advent Calendar 20215日目 の記事です。
昨日は @y-okudera さんの「 データの暗号化・復号とSwiftでの実装の仕方について改めて調べてみた 」でした!

ドラッグ&ドロップで並び替えしたい

リスト表示において、アイテムを長押しするとドラッグ&ドロップで並び替えができる、というのは結構あるあるな機能ではないでしょうか?

RecyclerViewではItemTouchHelperを使うことで、ドラッグ&ドロップでの並び替えを実装することができました。

しかしながら、Jetpack Composeにおいては、現状ドラッグ&ドロップはサポートされておりません。
一応ロードマップには「ドラッグ&ドロップのサポート」が記載されているので、今後公式にサポートされるものと思われます。

「公式にサポートされるまで待てないよ…!」という欲求に駆られたため、今回はライブラリの力に頼って :upside_down: ドラッグ&ドロップでの並び替えを実現してみたいと思います。

:pray: aclassen/ComposeReorderable :pray:

LazyListLazyColumnLazyRow )のドラッグ&ドロップでの並び替えを可能にするライブラリです。

こちらのライブラリの使い方を説明していきたいと思います。

Before

シンプルな LazyColumn を使った画面に、 ComposeReorderable を用いたドラッグ&ドロップでの並び替えを追加していくことを想定します。

対応前のコード↓

    Scaffold(
        topBar = {
            TopAppBar(title = { Text("Sample") })
        }
    ) {
        val data = remember {
            mutableStateOf(List(100) { "$it" })
        }

        LazyColumn {
            items(data.value) { item ->
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable {}
                        .padding(40.dp),
                ) {
                    Text(item)
                }
                Divider(thickness = 1.dp)
            }
        }
    }

画面としては↓のような感じです。

1. dependenciesへの追加

dependencies に追加します。記事作成時では 0.7.4 が最新版でした。

+    implementation "org.burnoutcrew.composereorderable:reorderable:0.7.4"

2. LazyColumnへ設定追加

rememberReorderState() を呼び出し ReorderableState を作成します。

② 作成した ReorderableStatelistState を、 LazyColumnstate に指定します。

ComposeReorderable の拡張である reorderable Modifierも設定します。

-        LazyColumn {
+        val reorderState = rememberReorderState() // ①
+
+        LazyColumn(
+            state = reorderState.listState, // ②
+            modifier = Modifier // ③
+                .reorderable(
+                    state = reorderState,
+                    onMove = { from, to -> /* TODO */ },
+                ),
+        ) {

3. リストのアイテムへの設定追加

リストの各アイテムの Modifier へ設定を追加します。

① 並び替え可能なアイテムであることを示すために、detectReorder(state) or detectReorderAfterLongPress(state) を指定します。
detectReorderAfterLongPress(state) の場合は、長押しで並び替えが起きるようになるので、今回はこちらを指定します。

draggedItem の指定も追加します。
引数の offset には reorderState.offsetByKey or reorderState.offsetByIndex を使って指定します。

         ) {
-            items(data.value) { item ->
+            itemsIndexed(data.value) { index, item ->
                 Box(
                     modifier = Modifier
                         .fillMaxWidth()
                         .clickable { /**/ }
+                        .detectReorderAfterLongPress(reorderState) // ①
+                        .draggedItem(reorderState.offsetByIndex(index)) // ②
                         .padding(40.dp),
                 ) {
                     Text(item)

一応この時点で、ドラッグ&ドロップは動くようになっています…!
ただ、データ自体が変わっていないため、ドラッグしているアイテムがおかしなことになっているので、2で /* TODO */ にしていた onMove の処理を実装します。

1638551030.gif

4. データの更新実装

ドラッグ&ドロップでアイテムを動かし、他のアイテムと切り替わるタイミングで onMove が呼ばれるので、そのタイミングでデータの更新をしてあげる必要があります。

moveComposeReorderable に入っている拡張関数です。

-          onMove = { from, to -> },
+          onMove = { from, to ->
+              data.value = data.value.toMutableList().apply { move(from.index, to.index) }
+          },

以上の工程で、ドラッグ&ドロップによる並び替えを実装することができました🎉
1638551845.gif

After

Beforeの対比としてAfterのコードも載せておきます。

    Scaffold(
        topBar = {
            TopAppBar(title = { Text("Sample") })
        }
    ) {
        val data = remember {
            mutableStateOf(List(100) { "$it" })
        }

        val reorderState = rememberReorderState()

        LazyColumn(
            state = reorderState.listState,
            modifier = Modifier
                .reorderable(
                    state = reorderState,
                    onMove = { from, to ->
                        data.value = data.value.toMutableList().apply { move(from.index, to.index) }
                    },
                ),
        ) {
            itemsIndexed(data.value) { index, item ->
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable {}
                        .detectReorder(reorderState)
                        .draggedItem(reorderState.offsetByIndex(index))
                        .padding(40.dp),
                ) {
                    Text(item)
                }
                Divider(thickness = 1.dp)
            }
        }
    }

終わりに

(ただのライブラリの使い方の説明記事になってしまいましたが…)
Jetpack Composeでドラッグ&ドロップの並び替えを実現することができました。
「公式にサポートされるまで待てないよ…!」という方の参考になれば幸いです。

15
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
4