Android 入门教程
Compose 手势处理
本教程共 100 篇 · 第 53 篇 · 更新于 2026-07-28 · 约 7 分钟阅读
AndroidAndroid 入门教程Jetpack Compose手势clickablepointerInput拖拽滑动
53. Compose 手势处理
本节目标:掌握 Compose 的各种手势处理方式,学会点击、长按、拖拽、滑动、缩放旋转等交互。
clickable 点击
最简单的手势,前面已多次用到:
Text(
"点我",
modifier = Modifier.clickable {
Log.d("Click", "被点击了")
}
)
clickable 自带涟漪效果和按压反馈。还能配置点击行为:
Modifier.clickable(
onClickLabel = "删除", // 无障碍读屏标签
role = Role.Button, // 无障碍角色
onClick = { /* 处理 */ }
)
combinedClickable 点击 + 长按
同时处理单击和长按:
@OptIn(ExperimentalFoundationApi::class)
Text(
"长按我试试",
modifier = Modifier.combinedClickable(
onClick = { Log.d("Gesture", "单击") },
onLongClick = { Log.d("Gesture", "长按") },
onDoubleClick = { Log.d("Gesture", "双击") }
)
)
Note
combinedClickable是实验性 API,加@OptIn(ExperimentalFoundationApi::class)。它一次性处理单击、双击、长按三种手势。
pointerInput 底层手势
pointerInput 是更底层的手势处理入口,能监听原始指针事件:
@Composable
fun TouchTracker() {
Box(
modifier = Modifier
.size(200.dp)
.background(Color.LightGray)
.pointerInput(Unit) {
awaitEachGesture {
val down = awaitFirstDown()
Log.d("Touch", "按下: ${down.position}")
while (true) {
val event = awaitPointerEvent()
event.changes.forEach { change ->
Log.d("Touch", "移动: ${change.position}")
}
}
}
}
)
}
一般不需要这么底层,Compose 提供了更高级的检测函数。
detectDragGestures 拖拽
@Composable
fun DraggableBox() {
var offset by remember { mutableStateOf(Offset.Zero) }
Box(
modifier = Modifier
.offset { IntOffset(offset.x.toInt(), offset.y.toInt()) }
.size(80.dp)
.background(Color.Blue, CircleShape)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consume()
offset += dragAmount
}
}
)
}
detectDragGestures检测拖拽手势,每次移动回调dragAmount(偏移量)。Modifier.offset { }用 lambda 版本支持动态位置。change.consume()标记事件已消费,避免冒泡。
detectDragGesturesAfterLongPress
长按后才能拖拽(类似长按图标进入编辑模式):
.pointerInput(Unit) {
detectDragGesturesAfterLongPress { change, dragAmount ->
change.consume()
offset += dragAmount
}
}
swipeable 滑动
做滑动开关、滑动删除:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SwipeToDismiss() {
val swipeState = remember {
AnchoredDraggableState(
initialValue = 0,
positionalThreshold = { distance -> distance * 0.5f },
velocityThreshold = { 125.dp.toPx() },
animationSpec = tween()
)
}
// 设置锚点:0 = 关闭,1 = 打开
// 具体实现需要配合 Box 和 offset
}
Tip
swipeable在新版 Compose 中已被AnchoredDraggable替代。适合做侧滑菜单、滑动删除等「在几个位置之间吸附」的效果。如果只是简单的水平/垂直拖拽,用detectHorizontalDragGestures更直接。
transformable 缩放和旋转
图片查看器那种双指缩放旋转:
@Composable
fun ZoomableImage() {
var scale by remember { mutableStateOf(1f) }
var rotation by remember { mutableStateOf(0f) }
var offset by remember { mutableStateOf(Offset.Zero) }
Box(
modifier = Modifier
.clipToBounds()
.pointerInput(Unit) {
detectTransformGestures { _, pan, zoom, rotationChange ->
scale *= zoom
rotation += rotationChange
offset += pan
}
}
) {
Image(
painter = painterResource(R.drawable.photo),
contentDescription = null,
modifier = Modifier
.graphicsLayer(
scaleX = scale,
scaleY = scale,
rotationZ = rotation,
translationX = offset.x,
translationY = offset.y
)
)
}
}
detectTransformGestures 回调四个值:
pan:平移量zoom:缩放倍率(1 = 不变,>1 放大)rotation:旋转角度centroid:手势中心点
Note
graphicsLayer用硬件加速做变换,比直接改 Modifier 属性性能好。缩放、旋转、平移都通过它应用。
scrollable 滚动
让组件可滚动:
@Composable
fun ScrollableColumn() {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
) {
repeat(50) {
Text("第 $it 项", modifier = Modifier.padding(16.dp))
}
}
}
verticalScroll 是最简单的滚动方式。LazyColumn 自带滚动,不需要这个。
滚动到指定位置
val scrollState = rememberScrollState()
val scope = rememberCoroutineScope()
Column(modifier = Modifier.verticalScroll(scrollState)) {
// 内容
}
Button(onClick = {
scope.launch {
scrollState.animateScrollTo(1000) // 平滑滚动到位置
}
}) {
Text("滚动到底部")
}
detectTapGestures 点击手势检测
比 clickable 更灵活的点击检测:
.pointerInput(Unit) {
detectTapGestures(
onTap = { offset -> Log.d("Tap", "点击 $offset") },
onDoubleTap = { offset -> Log.d("Tap", "双击") },
onLongPress = { offset -> Log.d("Tap", "长按") }
)
}
手势选择指南
| 手势需求 | 推荐方案 |
|---|---|
| 普通点击 | clickable |
| 单击 + 双击 + 长按 | combinedClickable 或 detectTapGestures |
| 拖拽 | detectDragGestures |
| 滑动吸附 | AnchoredDraggable |
| 缩放旋转 | detectTransformGestures |
| 滚动 | verticalScroll / LazyColumn |
Warning
pointerInput的 key 参数很重要。如果手势逻辑依赖某个变量,把它作为 key,变量变化时手势检测会重新初始化。否则可能用到旧值。
小结
Compose 手势处理从简单到复杂:clickable 做点击,combinedClickable 做多手势,pointerInput + detectXxxGestures 做拖拽缩放旋转。verticalScroll 做滚动。graphicsLayer 做高性能变换。比传统 View 的 onTouchEvent 简洁太多。下一节学习副作用。