许多可组合项都内置了对点按或点击的支持,并包含一个 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
行为:
@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
- Kotlin 对 Jetpack Compose 的支持