许多可组合项都内置支持点按或点击,并包含一个 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
方法结合使用。
@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