许多可组合项都内置支持点击,并包含一个 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
为您的可组合项添加了有用的功能。它们在交互时显示视觉指示,响应悬停,并包含焦点、键盘和辅助功能支持。但是,这种额外的行为并不总是理想的。
让我们看一下图像详情屏幕。背景应该半透明,用户应该能够点击该背景以关闭详情屏幕
在这种情况下,该背景不应该在交互时有任何视觉指示,不应该响应悬停,不应该可聚焦,并且它对键盘和辅助功能事件的响应与典型可组合项的响应不同。与其尝试调整 clickable
行为,不如降低抽象级别,并直接使用 pointerInput
修饰符结合 detectTapGestures
方法
@OptIn(ExperimentalComposeUiApi::class) @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 } )
为您推荐
- 注意:当 JavaScript 关闭时显示链接文本
- 了解手势
- Compose 中的 Material Design 2
- Jetpack Compose 的 Kotlin