今天大概会聊到的范围
- 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)
}
}
}
}
在 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 回到原先的位置的行为。
我希望当手指离开萤幕的时候,可以做回弹的动作。因此,我希望在 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:
>>: Day13-JDK堆内存快照工具-jmap(三)自动导出内存映像文件
这功能对於浏览器来说,应该是个没人(或很少人)想过会存在的功能。 从十几二十年前开始有浏览器以来,浏...
DAY27 MongoDB Time Series Collection 什麽是 Time seri...
linebot其实还有许多功能等待我去了解, 譬如说定时推播及许许多多引用在别的地方的套件, 在这一...
在Java程序设计中,有时一维阵列无法计算较多且较复杂的,这时我们需要二维阵列,例如要产生一个阵列A...
这个东东主要的概念来自 Martin Fowler 所写的 《 Presentation Model...