Nhiều thành phần kết hợp đã có hỗ trợ tích hợp cho thao tác nhấn hoặc nhấp và bao gồm một lambda onClick
. Ví dụ: bạn có thể tạo một Surface
có thể nhấp bao gồm tất cả hành vi Material Design phù hợp để tương tác với các nền tảng:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Tuy nhiên, thao tác nhấp chuột không phải là cách duy nhất để người dùng tương tác với các thành phần kết hợp. Trang này tập trung vào các cử chỉ liên quan đến một con trỏ, trong đó vị trí của con trỏ đó không quan trọng đối với việc xử lý sự kiện đó. Bảng sau đây liệt kê các loại cử chỉ này:
Cử chỉ |
Nội dung mô tả |
Nhấn (hoặc nhấp) |
Con trỏ di chuyển xuống rồi lên |
Nhấn đúp |
Con trỏ di chuyển xuống, lên, xuống, lên |
Nhấn và giữ |
Con trỏ di chuyển xuống và được giữ trong thời gian dài hơn |
Hình ảnh trên báo chí |
Con trỏ di chuyển xuống |
Phản hồi khi nhấn hoặc nhấp
clickable
là đối tượng sửa đổi thường dùng để tạo một thành phần kết hợp phản ứng với các thao tác nhấn hoặc nhấp. Đối tượng sửa đổi này cũng bổ sung thêm các tính năng khác, chẳng hạn như hỗ trợ tiêu điểm, di chuột và di bút cảm ứng, cũng như chỉ báo hình ảnh có thể tuỳ chỉnh khi nhấn. Đối tượng sửa đổi phản hồi "lượt nhấp" theo nghĩa rộng nhất của từ này – không chỉ bằng chuột hoặc ngón tay, mà còn bằng các sự kiện nhấp thông qua phương thức nhập bằng bàn phím hoặc khi sử dụng dịch vụ hỗ trợ tiếp cận.
Hãy tưởng tượng một lưới hình ảnh, trong đó một hình ảnh hiển thị toàn màn hình khi người dùng nhấp vào hình ảnh đó:
Bạn có thể thêm đối tượng sửa đổi clickable
vào từng mục trong lưới để triển khai hành vi này:
@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 } ) } }
Đối tượng sửa đổi clickable
cũng thêm hành vi bổ sung:
interactionSource
vàindication
, vẽ gợn sóng theo mặc định khi người dùng nhấn vào thành phần kết hợp. Tìm hiểu cách tuỳ chỉnh các thông báo này trên trang Xử lý lượt tương tác của người dùng.- Cho phép các dịch vụ hỗ trợ tiếp cận tương tác với phần tử bằng cách đặt thông tin ngữ nghĩa.
- Hỗ trợ tương tác bằng bàn phím hoặc cần điều khiển bằng cách cho phép tiêu điểm và nhấn vào
Enter
hoặc giữa d-pad để tương tác. - Tạo phần tử có thể di chuột để phần tử đó phản hồi khi chuột hoặc bút cảm ứng di chuột qua phần tử đó.
Nhấn và giữ để hiển thị trình đơn theo bối cảnh
combinedClickable
cho phép bạn thêm hành vi nhấn đúp hoặc nhấn và giữ ngoài hành vi nhấp chuột thông thường. Bạn có thể sử dụng combinedClickable
để hiển thị trình đơn theo bối cảnh khi người dùng chạm và giữ hình ảnh dạng lưới:
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 } ) }
Tốt nhất là bạn nên đưa phản hồi xúc giác vào khi người dùng nhấn và giữ các phần tử. Đó là lý do đoạn mã này bao gồm lệnh gọi performHapticFeedback
.
Đóng một thành phần kết hợp bằng cách nhấn vào một màn hình chờ
Trong các ví dụ ở trên, clickable
và combinedClickable
thêm chức năng hữu ích vào các thành phần kết hợp. Các thành phần này hiển thị chỉ báo trực quan về hoạt động tương tác, phản hồi khi di chuột và bao gồm tiêu điểm, bàn phím và hỗ trợ tiếp cận. Tuy nhiên, hành vi bổ sung này không phải lúc nào cũng mong muốn.
Hãy xem màn hình chi tiết hình ảnh. Nền phải có độ bán trong suốt và người dùng có thể nhấn vào nền đó để đóng màn hình chi tiết:
Trong trường hợp này, nền đó không được có bất kỳ chỉ báo hình ảnh nào về tương tác, không được phản hồi khi di chuột, không được lấy tiêu điểm và phản hồi của nền đó đối với bàn phím và các sự kiện hỗ trợ tiếp cận khác với phản hồi của một thành phần kết hợp thông thường. Thay vì cố gắng điều chỉnh hành vi của clickable
, bạn có thể hạ cấp xuống cấp độ trừu tượng thấp hơn và trực tiếp sử dụng đối tượng sửa đổi pointerInput
kết hợp với phương thức 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)) ) }
Là khoá của đối tượng sửa đổi pointerInput
, bạn truyền hàm lambda onClose
. Thao tác này sẽ tự động thực thi lại lambda, đảm bảo lệnh gọi lại phù hợp được gọi khi người dùng nhấn vào scrim.
Nhấn đúp để thu phóng
Đôi khi, clickable
và combinedClickable
không cung cấp đủ thông tin để phản hồi tương tác đúng cách. Ví dụ: các thành phần kết hợp có thể cần quyền truy cập vào vị trí trong giới hạn của thành phần kết hợp nơi diễn ra hoạt động tương tác.
Hãy xem lại màn hình chi tiết hình ảnh. Phương pháp hay nhất là cho phép người dùng phóng to hình ảnh bằng cách nhấn đúp:
Như bạn có thể thấy trong video, thao tác thu phóng diễn ra xung quanh vị trí của sự kiện nhấn. Kết quả sẽ khác nhau khi chúng ta phóng to phần bên trái của hình ảnh so với phần bên phải. Chúng ta có thể sử dụng đối tượng sửa đổi pointerInput
kết hợp với detectTapGestures
để kết hợp vị trí nhấn vào phép tính của chúng ta:
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 } )
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Tìm hiểu về cử chỉ
- Material Design 2 trong Compose
- Kotlin cho Jetpack Compose