iT邦幫忙

2022 iThome 鐵人賽

DAY 20
1

前言


今天要來實踐如何在 Lazy Layout 中滑動 item。

image

支援庫 ComposeReorderable

昨天開心的研究完手勢互動,突然發現在 Lazy Grid Layout 中還沒有支援拖曳排序,於是乎我找了第三方開源庫 ComposeReorderable

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

他支援 Jetpack Compose 的 LazyList 和 LazyGrid 元件透過 modifier 擴充函數來做到 drag 和 drop 互動改變 list 的順序。

改寫範例

首先根據 ComposeReorderable 的範例,改寫如下。

import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.burnoutcrew.reorderable.*

@Composable
fun LazyGridDragAndDrop() {

		//顯示 card 的文字內容 index 
    val data = remember { mutableStateOf(List(100) { "Item $it" }) }

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

    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        state = state.gridState,
        modifier = Modifier.reorderable(state) //Liberary 做的 modifier extention
    ) {
        items(data.value, { it }) { item ->
            ReorderableItem(
                state,
                key = item,
                defaultDraggingModifier = Modifier
            ) { isDragging ->
                Card(
                    modifier = Modifier
                        .detectReorderAfterLongPress(state)
                        .aspectRatio(1f)
                        .padding(8.dp),
                ) {
                    Text(
                        text = item,
                        modifier = Modifier.fillMaxWidth(),
                        textAlign = TextAlign.Center
                    )
                }
            }
        }
    }
}

從文章最開始的動圖可看看到,做出來的效果在垂直面上可以看到交換的效果,但水平面上卻會直接換過去。
為了解決這個問題我會想知道 Modifier.reorderable(state)裡面做了什麼,是不是有哪個步驟判斷手勢的位置去做了動畫效果。

嘗試理解內部實作


他主要是在 modifier 做了extenstion funtion reorderable() 。裡頭利用了 ModifierpointerInput()

fun Modifier.reorderable(
    state: ReorderableState<*>
) = then(
    Modifier.pointerInput(Unit) {
        forEachGesture {
            val dragStart = state.interactions.receive()
            val down = awaitPointerEventScope {
                currentEvent.changes.fastFirstOrNull { it.id == dragStart.id }
            }
            if (down != null && state.onDragStart(down.position.x.toInt(), down.position.y.toInt())) {
                dragStart.offset?.apply {
                    state.onDrag(x.toInt(), y.toInt())
                }
                detectDrag(
                    down.id,
                    onDragEnd = {
                        state.onDragCanceled()
                    },
                    onDragCancel = {
                        state.onDragCanceled()
                    },
                    onDrag = { change, dragAmount ->
                        change.consume()
                        state.onDrag(dragAmount.x.toInt(), dragAmount.y.toInt())
                    })
            }
        }
    })

這裡有些參數很類似昨天學的 interaction ,像是變數 dragStart 用到了 state.interactions 這裡回傳類型是庫中的 Channel<StartDrag> 在用 receive() 接收,這個寫法用到了 coroutines.channels 的概念。最後 dragStart 的類型是 StartDrag

detectDrag 追蹤Drag 的狀態。

onDrag 中用到了 change.consume() 是手勢中的消費手勢事件。

總結


要能看懂這個三方庫需要懂 gestureinteractioncoroutinesstate,全部研究出來可能就是另外 30天了… (頭髮都白了

參考

LazyColumn 拖动排序

ComposeReorderable

今日運動:
休息


上一篇
Day 19 使用者互動,如何在 compose 中監聽 click、drag 等動作
下一篇
Day 21 Compose Navigation 頁面切換
系列文
今年一定減成功!Jetpack Compose 做出重訓紀錄APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言