D14/ 怎麽做拉动的操作? - Draggable Gesture

今天大概会聊到的范围

  • draggable
  • pointerInput

前两天在 Feedy 上看文章顺便想灵感时,突然发现这个行为:

列表项目可以左右拖移并展示出更多功能。我觉得这个行为很适合现在的我来仿造学习:有 State 概念、有 Animation 又有 Gesture。

从最单纯的一个画面开始吧,简单的文字 + LazyColumn 组成的一个 List,仿造 Feedy 的列表。

@Composable
fun Item(title: String = "", content: String = "") {
    Card(
        backgroundColor = black,
        modifier = Modifier
            .clip(RoundedCornerShape(4.dp))
            .fillMaxWidth()
            .padding(horizontal = 16.dp, vertical = 6.dp)
    ) {
        Column {
            
            Text(
                title,
                color = Color.White,
                style = titleStyle,
            )
            
            Text(
                content,
                color = Color.White,
                style = contentStyle,
                softWrap = true,
                maxLines = 4,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}


@Composable
fun ListScreen() {
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 8.dp)
            .background(color = blackBgColor)
    ) {
        
        val repo = RandomWordRepo()
        
        LazyColumn {
            items(repo.randomContent) {
                val (title, content) = it
                Item(title = title, content = content)
            }
        }
        
    }
}

https://ithelp.ithome.com.tw/upload/images/20210928/20141597ysyBRSsBBA.png

拉动 Item 的行为

在 Gesture modifier 中,我们有 draggable 这个 modifier 可以表达拖拉的行为。

@Composable
fun DraggableItem(title: String = "", content: String = "") {
    
    Box(modifier = Modifier.draggable(
        orientation = Orientation.Horizontal,
        state = rememberDraggableState {
            Log.d("DraggableItem", "DraggableItem: $it")
        }
    )) {
        Item(title = title, content = content)
    }
}

在使用 draggable 时,我们要提供两个资料:orientation 表示可以拉动的维度(水平还是垂直)、state 则是拉动距离的资料载体,使用 rememberDraggableState 可以取得 delta 值 (拉动距离) 作为 callback 使用

目前这个 Item 已经可以被拉动了(Log 会有资料),但是画面不会跟着动。这时我们要将拉动距离和物件的位置做连动

@Composable
fun DraggableItem(title: String = "", content: String = "") {
    
    // 增加一个 state 纪录 offsetX
    var offsetX by remember { mutableStateOf(0f) }
    
    Box(modifier = Modifier
        .offset { IntOffset(x = offsetX.roundToInt(), y = 0) }
        .draggable(
            orientation = Orientation.Horizontal,
            state = rememberDraggableState {
                offsetX += it   // <--- 每次都将 drag 的距离加上 x offset
            }
        )) {
        Item(title = title, content = content)
    }
}

现在我们可以拉动 Item 了,但这个 Item 可以任意地被拉动,这不是我们所希望的。所以我在 offset {} 加上 maxOffset 的判断

.offset {
        
    val maxOffset = 100.dp.toPx()
        
    val x =
        when {
            offsetX > maxOffset -> maxOffset
            offsetX < -maxOffset -> -maxOffset
            else -> offsetX
        }
        
    IntOffset(
        x = x.roundToInt(),
        y = 0
    )
}

让 Item 回弹

现在 Item 可以被拉动了,但是并不会回到原位。因此我们需要一个让 Item 回到原先的位置的行为。

我希望当手指离开萤幕的时候,可以做回弹的动作。因此,我希望在 drag 结束时将 offsetX 设定回 0

.draggable(
    orientation = Orientation.Horizontal,
    state = rememberDraggableState {
        offsetX += it
    },
    onDragStopped = { offsetX = 0f }
)

整个 drag 行为,也可以透过 pointerInput 来达成。pointerInput 是另一个 gesture modifier,他可以取得较 low-level 的互动数据并加以操作

加上动画

最後,我想在 Item 移动时加上动画。基本上我只需要将 offsetX 这个 float 透过 animateFloatAsState 做一次转换就可以了。

var offsetX by remember { mutableStateOf(0f) }
    
val offsetXAnimate by animateFloatAsState(targetValue = offsetX)

接下来我们将所有用到 offsetX 的部分都改成 offsetXAnimate 就大功告成了!


Reference:


<<:  Day-28 轻松使用Excel生成乱数并排名

>>:  Day13-JDK堆内存快照工具-jmap(三)自动导出内存映像文件

电子书阅读器上的浏览器 [Day14] 中文直排阅读模式

这功能对於浏览器来说,应该是个没人(或很少人)想过会存在的功能。 从十几二十年前开始有浏览器以来,浏...

DAY27 MongoDB Time Series Collection

DAY27 MongoDB Time Series Collection 什麽是 Time seri...

Day29 LineBot总结

linebot其实还有许多功能等待我去了解, 譬如说定时推播及许许多多引用在别的地方的套件, 在这一...

Day 20 2D Arrays

在Java程序设计中,有时一维阵列无法计算较多且较复杂的,这时我们需要二维阵列,例如要产生一个阵列A...

30-10 之Presentation Layer - MVVM ( Model-View-ViewModel )

这个东东主要的概念来自 Martin Fowler 所写的 《 Presentation Model...