많은 컴포저블은 탭 또는 클릭을 기본적으로 지원하며
onClick
람다. 예를 들어 클릭 가능한 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
또는 D패드의 중앙을 사용하여 상호작용할 수 있습니다. - 마우스 오버 시 마우스 또는 스타일러스에 반응하도록 요소를 마우스 오버 가능하게 합니다. 있습니다.
컨텍스트 메뉴를 표시하려면 길게 누르세요.
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
람다를 전달합니다. 이
자동으로 람다를 다시 실행하여 올바른 콜백이 호출되는지 확인합니다.
사용자가 스크림을 탭할 때
두 번 탭하여 확대/축소
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