点按并按下
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
许多可组合项都内置了对点按或点击的支持,并包含 onClick
lambda。例如,您可以创建一个可点击的 Surface
,其中包含适用于与 Surface 互动的所有 Material Design 行为:
Surface(onClick = { /* handle click */ }) {
Text("Click me!", Modifier.padding(24.dp))
}
不过,点击并非用户与可组合项互动的方式。本页将重点介绍涉及单个指针的手势,其中指针的位置对处理该事件而言并不重要。下表列出了这些类型的手势:
手势 |
说明 |
点按(或点击) |
指针先向下再向上 |
点按两次 |
指针依次向下、向上、向下、向上移动 |
长按 |
指针向下,并保持更长时间 |
新闻 |
指针向下 |
响应点按或点击
clickable
是一种常用的修饰符,可让可组合项响应点按或点击。此修饰符还添加了其他功能,例如支持聚焦、鼠标和触控笔悬停,以及在按下时显示可自定义的视觉指示。该修饰符会响应广义上的“点击”操作,不仅包括鼠标或手指点击,还包括通过键盘输入或使用无障碍服务时的点击事件。
假设有一个图片网格,其中某张图片在用户点击后会全屏显示:
您可以向网格中的每个项添加 clickable
修饰符,以实现此行为:
@Composable
private fun ImageGrid(photos: List<Photo>) {
var activePhotoId by rememberSaveable { mutableStateOf<Int?>(null) }
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) {
items(photos, { it.id }) { photo ->
ImageItem(
photo,
Modifier.clickable { activePhotoId = photo.id }
)
}
}
if (activePhotoId != null) {
FullScreenImage(
photo = photos.first { it.id == activePhotoId },
onDismiss = { activePhotoId = null }
)
}
}
clickable
修饰符还会添加其他行为:
interactionSource
和 indication
,默认情况下,当用户点按可组合项时,它们会绘制涟漪效果。如需了解如何对这些内容进行自定义,请参阅处理用户互动页面。
- 允许无障碍服务通过设置语义信息与元素互动。
- 支持键盘或操纵杆互动,方法是允许焦点,然后按
Enter
或方向键中心进行互动。
- 使元素可悬停,以便它响应鼠标或触控笔在其上方悬停。
长按以显示上下文菜单
除了正常的点击行为之外,您还可以使用 combinedClickable
添加双击或长按行为。您可以使用 combinedClickable
在用户轻触并按住网格图片时显示上下文菜单:
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) }
val haptics = LocalHapticFeedback.current
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) {
items(photos, { it.id }) { photo ->
ImageItem(
photo,
Modifier
.combinedClickable(
onClick = { activePhotoId = photo.id },
onLongClick = {
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
contextMenuPhotoId = photo.id
},
onLongClickLabel = stringResource(R.string.open_context_menu)
)
)
}
}
if (contextMenuPhotoId != null) {
PhotoActionsSheet(
photo = photos.first { it.id == contextMenuPhotoId },
onDismissSheet = { contextMenuPhotoId = null }
)
}
最佳实践是,在用户长按元素时提供触感反馈,因此该代码段包含 performHapticFeedback
调用。
通过点按遮罩关闭可组合项
在上面的示例中,clickable
和 combinedClickable
为可组合项添加了实用功能。此类广告素材会在互动时显示视觉指示,对悬停做出响应,并支持焦点、键盘和无障碍功能。但这种额外行为并不总是可取的。
我们来看看图片详情页面。背景应为半透明,并且用户应能够点按该背景以关闭详情页面:
在这种情况下,该背景在互动时不应显示任何视觉指示,不应响应悬停,不应可获得焦点,并且它对键盘和无障碍事件的响应与典型可组合项不同。您可以降级到较低的抽象级别,直接将 pointerInput
修饰符与 detectTapGestures
方法结合使用,而不是尝试调整 clickable
行为:
@Composable
private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) {
val strClose = stringResource(R.string.close)
Box(
modifier
// handle pointer input
.pointerInput(onClose) { detectTapGestures { onClose() } }
// handle accessibility services
.semantics(mergeDescendants = true) {
contentDescription = strClose
onClick {
onClose()
true
}
}
// handle physical keyboard input
.onKeyEvent {
if (it.key == Key.Escape) {
onClose()
true
} else {
false
}
}
// draw scrim
.background(Color.DarkGray.copy(alpha = 0.75f))
)
}
您可以将 onClose
lambda 作为 pointerInput
修饰符的键传递。这会自动重新执行 lambda,确保在用户点按遮罩时调用正确的回调。
点按两次即可缩放
有时,clickable
和 combinedClickable
中的信息不足以以正确的方式响应互动。例如,可组合项可能需要访问发生互动的可组合项边界内的某个位置。
我们再来看看图片详情页面。最佳实践是允许用户通过双击来放大图片:
如视频所示,系统会围绕点按事件的位置进行放大。放大图片的左侧部分与右侧部分时,结果会有所不同。我们可以将 pointerInput
修饰符与 detectTapGestures
结合使用,将点按位置纳入到计算中:
var zoomed by remember { mutableStateOf(false) }
var zoomOffset by remember { mutableStateOf(Offset.Zero) }
Image(
painter = rememberAsyncImagePainter(model = photo.highResUrl),
contentDescription = null,
modifier = modifier
.pointerInput(Unit) {
detectTapGestures(
onDoubleTap = { tapOffset ->
zoomOffset = if (zoomed) Offset.Zero else
calculateOffset(tapOffset, size)
zoomed = !zoomed
}
)
}
.graphicsLayer {
scaleX = if (zoomed) 2f else 1f
scaleY = if (zoomed) 2f else 1f
translationX = zoomOffset.x
translationY = zoomOffset.y
}
)
为您推荐
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-08-23。
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["没有我需要的信息","missingTheInformationINeed","thumb-down"],["太复杂/步骤太多","tooComplicatedTooManySteps","thumb-down"],["内容需要更新","outOfDate","thumb-down"],["翻译问题","translationIssue","thumb-down"],["示例/代码问题","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-23。"],[],[],null,["# Tap and press\n\nMany composables have built-in support for taps or clicks and include an\n`onClick` lambda. For example, you can create a clickable `Surface` that\nincludes all Material Design behavior appropriate for interaction with surfaces:\n\n\n```kotlin\nSurface(onClick = { /* handle click */ }) {\n Text(\"Click me!\", Modifier.padding(24.dp))\n}https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/pointerinput/TapAndPress.kt#L111-L113\n```\n\n\u003cbr /\u003e\n\nBut clicks are not the only way a user can interact with composables. This page\nfocuses on gestures that involve a single pointer, where the position of\nthat pointer is not significant for the handling of that event. The following\ntable lists these types of gestures:\n\n|----------------|--------------------------------------------------|\n| **Gesture** | **Description** |\n| Tap (or click) | Pointer goes down and then up |\n| Double tap | Pointer goes down, up, down, up |\n| Long-press | Pointer goes down, and is held for a longer time |\n| Press | Pointer goes down |\n\nRespond to tap or click\n-----------------------\n\n[`clickable`](/reference/kotlin/androidx/compose/foundation/package-summary#(androidx.compose.ui.Modifier).clickable(kotlin.Boolean,kotlin.String,androidx.compose.ui.semantics.Role,kotlin.Function0)) is a commonly used modifier that makes a composable react to\ntaps or clicks. This modifier also adds additional features, such as support for\nfocus, mouse and stylus hovering, and a customizable visual indication when\npressed. The modifier responds to \"clicks\" in the widest sense of the word-- not\nonly with mouse or finger, but also click events through keyboard input or when\nusing accessibility services.\n\nImagine a grid of images, where an image shows full-screen when a user\nclicks on it:\n\nYou can add the `clickable` modifier to each item in the grid to implement this\nbehavior:\n\n\n```kotlin\n@Composable\nprivate fun ImageGrid(photos: List\u003cPhoto\u003e) {\n var activePhotoId by rememberSaveable { mutableStateOf\u003cInt?\u003e(null) }\n LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) {\n items(photos, { it.id }) { photo -\u003e\n ImageItem(\n photo,\n Modifier.clickable { activePhotoId = photo.id }\n )\n }\n }\n if (activePhotoId != null) {\n FullScreenImage(\n photo = photos.first { it.id == activePhotoId },\n onDismiss = { activePhotoId = null }\n )\n }\n}https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/pointerinput/TapAndPress.kt#L119-L138\n```\n\n\u003cbr /\u003e\n\nThe `clickable` modifier also adds additional behavior:\n\n- `interactionSource` and `indication`, which draw a ripple by default when a user taps the composable. Learn how to customize these on the [Handling user\n interactions](/develop/ui/compose/touch-input/user-interactions/handling-interactions) page.\n- Allows accessibility services to interact with the element by setting the semantics information.\n- Supports keyboard or joystick interaction by allowing focus and pressing `Enter` or the center of the d-pad to interact.\n- Make the element hoverable, so it responds to the mouse or stylus hovering over it.\n\nLong-press to show a contextual context menu\n--------------------------------------------\n\n[`combinedClickable`](/reference/kotlin/androidx/compose/foundation/package-summary#(androidx.compose.ui.Modifier).combinedClickable(androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.foundation.Indication,kotlin.Boolean,kotlin.String,androidx.compose.ui.semantics.Role,kotlin.String,kotlin.Function0,kotlin.Function0,kotlin.Function0)) lets you add double tap or long-press behavior in\naddition to normal click behavior. You can use `combinedClickable` to show a\ncontext menu when a user touches and holds a grid image:\n\n\n```kotlin\nvar contextMenuPhotoId by rememberSaveable { mutableStateOf\u003cInt?\u003e(null) }\nval haptics = LocalHapticFeedback.current\nLazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) {\n items(photos, { it.id }) { photo -\u003e\n ImageItem(\n photo,\n Modifier\n .combinedClickable(\n onClick = { activePhotoId = photo.id },\n onLongClick = {\n haptics.performHapticFeedback(HapticFeedbackType.LongPress)\n contextMenuPhotoId = photo.id\n },\n onLongClickLabel = stringResource(R.string.open_context_menu)\n )\n )\n }\n}\nif (contextMenuPhotoId != null) {\n PhotoActionsSheet(\n photo = photos.first { it.id == contextMenuPhotoId },\n onDismissSheet = { contextMenuPhotoId = null }\n )\n}https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/pointerinput/TapAndPress.kt#L146-L171\n```\n\n\u003cbr /\u003e\n\nAs a best practice, you should include haptic feedback when the user\nlong-presses elements, which is why the snippet includes the\n`performHapticFeedback` invocation.\n\nDismiss a composable by tapping a scrim\n---------------------------------------\n\nIn the examples above, `clickable` and `combinedClickable` add useful\nfunctionality to your composables. They show a visual indication on interaction,\nrespond to hovering, and include focus, keyboard, and accessibility support. But\nthis extra behavior is not always desirable.\n\nLet's look at the image detail screen. The background should be semi-transparent\nand the user should be able to tap that background to dismiss the detail screen:\n\nIn this case, that background should not have any visual indication on\ninteraction, should not respond to hovering, should not be focusable, and its\nresponse to keyboard and accessibility events differ from that of a typical\ncomposable. Instead of trying to adapt the `clickable` behavior, you can drop\ndown to a lower abstraction level and directly use the `pointerInput` modifier\nin combination with the `detectTapGestures` method:\n\n\n```kotlin\n@Composable\nprivate fun Scrim(onClose: () -\u003e Unit, modifier: Modifier = Modifier) {\n val strClose = stringResource(R.string.close)\n Box(\n modifier\n // handle pointer input\n .pointerInput(onClose) { detectTapGestures { onClose() } }\n // handle accessibility services\n .semantics(mergeDescendants = true) {\n contentDescription = strClose\n onClick {\n onClose()\n true\n }\n }\n // handle physical keyboard input\n .onKeyEvent {\n if (it.key == Key.Escape) {\n onClose()\n true\n } else {\n false\n }\n }\n // draw scrim\n .background(Color.DarkGray.copy(alpha = 0.75f))\n )\n}https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/pointerinput/TapAndPress.kt#L293-L322\n```\n\n\u003cbr /\u003e\n\nAs the key of the `pointerInput` modifier you pass the `onClose` lambda. This\nautomatically re-executes the lambda, making sure the right callback is called\nwhen the user taps the scrim.\n\nDouble tap to zoom\n------------------\n\nSometimes `clickable` and `combinedClickable` do not include enough information\nto respond to the interaction in the correct way. For example, composables might\nneed access to the position within the composable's bounds where the interaction\ntook place.\n\nLet's look at the image detail screen again. A best practice is to make it\npossible to zoom in on the image by double tapping:\n\nAs you can see in the video, zooming in occurs around the position of the tap\nevent. The result is different when we zoom in on the left part of the image\nversus the right part. We can use the `pointerInput` modifier in combination\nwith the `detectTapGestures` to incorporate the tap position into our\ncalculation:\n\n\n```kotlin\nvar zoomed by remember { mutableStateOf(false) }\nvar zoomOffset by remember { mutableStateOf(Offset.Zero) }\nImage(\n painter = rememberAsyncImagePainter(model = photo.highResUrl),\n contentDescription = null,\n modifier = modifier\n .pointerInput(Unit) {\n detectTapGestures(\n onDoubleTap = { tapOffset -\u003e\n zoomOffset = if (zoomed) Offset.Zero else\n calculateOffset(tapOffset, size)\n zoomed = !zoomed\n }\n )\n }\n .graphicsLayer {\n scaleX = if (zoomed) 2f else 1f\n scaleY = if (zoomed) 2f else 1f\n translationX = zoomOffset.x\n translationY = zoomOffset.y\n }\n)https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/pointerinput/TapAndPress.kt#L328-L351\n```\n\n\u003cbr /\u003e\n\nRecommended for you\n-------------------\n\n- Note: link text is displayed when JavaScript is off\n- [Understand gestures](/develop/ui/compose/touch-input/pointer-input/understand-gestures)\n- [Material Design 2 in Compose](/develop/ui/compose/designsystems/material)\n- [Kotlin for Jetpack Compose](/develop/ui/compose/kotlin)"]]