Wear OS용 Compose로 목록 사용


목록을 사용하면 사용자는 Wear OS 기기에서 여러 선택 항목 중 하나를 고를 수 있습니다.

많은 Wear OS 기기가 원형 화면을 사용하므로 화면의 상단과 하단 근처에 표시되는 목록 항목을 보기가 어렵습니다. 이러한 이유로 Wear OS용 Compose에는 크기 조정 및 모핑 애니메이션을 지원하는 TransformingLazyColumn이라는 LazyColumn 클래스의 한 가지 버전이 포함되어 있습니다. 항목이 가장자리로 이동하면 항목이 작아지고 흐려집니다.

추천되는 크기 조절 및 스크롤 효과를 적용하려면 다음 단계를 따르세요.

  1. Modifier.transformedHeight를 사용하여 항목이 화면을 스크롤할 때 Compose가 높이 변경을 계산하도록 허용합니다.
  2. transformation = SurfaceTransformation(transformationSpec)를 사용하여 항목 콘텐츠의 축소 등 시각적 효과를 적용합니다.
  3. transformation를 매개변수로 사용하지 않는 구성요소(예: Text)에는 맞춤 TransformationSpec를 사용합니다.

다음 애니메이션은 목록 요소가 화면 상단과 하단에 접근할 때 크기가 조정되고 모양이 변경되는 방식을 보여줍니다.

다음 코드 스니펫은 TransformingLazyColumn 레이아웃을 사용하여 다양한 Wear OS 화면 크기에서 보기 좋은 콘텐츠를 만드는 목록을 만드는 방법을 보여줍니다.

이 스니펫은 minimumVerticalContentPadding 수정자의 사용도 보여줍니다. 목록의 상단과 하단에 올바른 패딩을 적용하려면 목록 항목에 minimumVerticalContentPadding 수정자를 설정해야 합니다.

스크롤 표시기를 표시하려면 ScreenScaffoldTransformingLazyColumn 사이의 columnState를 공유하세요.

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(
    scrollState = columnState
) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding
    ) {
        item {
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text(text = "Header")
            }
        }
        // ... other items
        item {
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec),
                onClick = { /* ... */ },
                icon = {
                    Icon(
                        imageVector = Icons.Default.Build,
                        contentDescription = "build",
                    )
                },
            ) {
                Text(
                    text = "Build",
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
    }
}

맞추기 및 플링 효과 추가

스냅을 사용하면 사용자가 스크롤 또는 플링 동작을 완료할 때 목록이 특정 지점(일반적으로 화면 중앙)에 정확하게 배치된 항목과 함께 정착됩니다. 원형 화면에서는 항목이 중앙에서 멀어질수록 크기가 조정되고 변형되므로, 스냅은 가장 관련성 높은 항목이 최적의 보기 영역에서 완전히 표시되고 읽을 수 있도록 하는 데 특히 유용합니다.

스냅 앤 플링 동작을 추가하려면 flingBehavior 매개변수를 TransformingLazyColumnDefaults.snapFlingBehavior(columnState)로 설정합니다. 물리적 용두나 베젤을 사용할 때 일관된 환경을 제공하려면 RotaryScrollableDefaults.snapBehavior(columnState)를 사용하여 rotaryScrollableBehavior를 일치하도록 설정합니다.

val columnState = rememberTransformingLazyColumnState()
ScreenScaffold(scrollState = columnState) {
    TransformingLazyColumn(
        state = columnState,
        flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState),
        rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState)
    ) {
        // ...
        // ...
    }
}

역방향 레이아웃

기본적으로 스크롤 가능한 목록은 상단에 고정됩니다. 사용자가 표준 목록의 하단으로 스크롤한 상태에서 새 항목이 끝에 추가되면 목록은 현재 항목에 대한 사용자의 뷰를 유지합니다. 예를 들어 사용자가 화면 하단에서 항목 10을 보고 있고 항목 11이 추가되면 뷰는 항목 10에 계속 포커스가 맞춰져 있고 항목 11은 현재 뷰 아래 화면에 표시됩니다.

메시지 애플리케이션이나 실시간 로그와 같은 사용 사례에서는 이 동작이 바람직하지 않습니다. 새 항목이 도착하면 사용자는 이미 목록 하단에 있는 경우 최신 콘텐츠를 즉시 확인하고 싶어 합니다. 한 번에 여러 항목이 도착하면 목록은 하단에 가장 최근 항목을 표시하도록 건너뛰어야 합니다 (즉, 사용자가 다시 위로 스크롤하지 않는 한 일부 중간 항목이 전혀 표시되지 않을 수 있음).

이러한 사용 사례를 지원하기 위해 TransformingLazyColumn에서는 reverseLayout = true을 설정하여 레이아웃을 반전할 수 있습니다. 이렇게 하면 목록의 앵커가 상단에서 하단으로 변경됩니다.

편의를 위해 reverseLayout = true을 설정하면 항목의 시각적 순서와 스크롤 동작의 방향도 반전됩니다.

  • 항목은 하단에서 상단으로 구성됩니다. 즉, 색인 0이 화면 하단에 표시됩니다.
  • 위로 스크롤하면 색인이 높은 항목이 표시됩니다.

역방향 레이아웃과 함께 스냅 및 플링 동작을 추가하려면 다음 스니펫과 같이 flingBehaviorrotaryScrollableBehavior를 결합하면 됩니다.

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(scrollState = columnState) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding,
        reverseLayout = true,
        modifier = Modifier.fillMaxWidth()
    ) {
        items(10) { index ->
            Button(
                label = {
                    Text(
                        text = "Item ${index + 1}"
                    )
                },
                onClick = {},
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            )
        }
        item {
            // With reverseLayout = true, the last item declared appears at the top.
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text("Header")
            }
        }
    }
}

다음 이미지는 일반 목록과 역순 목록의 차이를 보여줍니다.

일반 레이아웃이 적용된 TransformingLazyColumn으로, Item 1이 상단에 표시되고 항목이 오름차순으로 표시됩니다.
그림 1. 콘텐츠가 위에서 아래로 채워지는 표준 목록 레이아웃입니다.
레이아웃이 반전된 TransformingLazyColumn으로, Item 1이 하단에 표시되고 항목이 상단으로 갈수록 내림차순으로 표시됩니다.
그림 2. 콘텐츠가 하단에서 상단으로 채워지는 역순 목록 레이아웃입니다.