輕觸並按下
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
許多可組合項都內建輕觸或點擊支援功能,並包含 onClick
lambda。舉例來說,您可以建立可點選的 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))
)
}
pointerInput
修飾符的鍵,您會傳遞 onClose
lambda。這會自動重新執行 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 和/或其關係企業的商標或註冊商標。
上次更新時間: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"]],["上次更新時間: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)"]]