탭한 후 누르기

많은 컴포저블은 탭 또는 클릭을 기본적으로 지원하며 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 수정자는 추가 동작도 추가합니다.

  • interactionSourceindication: 사용자가 컴포저블을 탭합니다. 이를 맞춤설정하는 방법에 대한 자세한 내용은 사용자 처리 상호작용 페이지를 참조하세요.
  • 접근성 서비스에서 의미론적 정보를 생성합니다
  • 포커스를 허용하고 눌러 키보드 또는 조이스틱 상호작용 지원 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 호출

스크림을 탭하여 컴포저블 닫기

위의 예에서 clickablecombinedClickable는 유용한 기능을 추가해야 합니다. 상호작용을 시각적으로 표시하므로 마우스 오버에 응답하고 포커스, 키보드 및 접근성 지원을 포함합니다. 하지만 이 추가 동작이 항상 바람직한 것은 아닙니다.

이미지 세부정보 화면을 살펴보겠습니다. 배경은 반투명해야 합니다. 사용자는 이 배경을 탭하여 세부정보 화면을 닫을 수 있어야 합니다.

이 경우 배경색에 시각적 표시가 있어서는 안 됩니다. 있어야 하고, 마우스 오버에 응답해서는 안 되고, 포커스 불가능해야 하고, 키보드 및 접근성 이벤트에 대한 응답이 일반적인 있습니다. 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 람다를 전달합니다. 이 자동으로 람다를 다시 실행하여 올바른 콜백이 호출되는지 확인합니다. 사용자가 스크림을 탭할 때

두 번 탭하여 확대/축소

clickablecombinedClickable에 포함된 정보가 충분하지 않은 경우가 있습니다. 상호작용에 올바른 방식으로 응답해야 합니다 예를 들어 컴포저블은 상호작용이 이루어지는 컴포저블의 경계 내 위치에 액세스해야 합니다. 일어난 일이었습니다

이미지 세부정보 화면을 다시 살펴보겠습니다. 가장 좋은 방법은 두 번 탭하여 이미지를 확대할 수 있습니다.

동영상에서 볼 수 있듯이 탭한 위치 주변이 확대됩니다. 이벤트를 처리합니다. 이미지의 왼쪽 부분을 확대하면 결과가 달라집니다. 오른쪽 부분과 비교해 볼 수 있습니다. 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
        }
)