TV용 스크롤 가능한 레이아웃 만들기

TV 앱의 경우 탐색 환경은 효율적인 포커스 기반 탐색에 의존합니다. 표준 Compose Foundation 지연 레이아웃을 사용하면 포커스 기반 스크롤을 자동으로 처리하여 활성 항목을 표시 상태로 유지하는 성능이 우수한 세로 및 가로 목록을 만들 수 있습니다.

TV에 최적화된 기본 스크롤 동작

Compose Foundation 1.7.0부터 표준 지연 레이아웃 (예: LazyRowLazyColumn)에는 포커스 위치 지정 기능에 대한 기본 지원이 포함됩니다. 이 방법은 사용자가 집중하는 항목을 계속 표시하고 직관적으로 배치하는 데 도움이 되므로 TV 앱용 카탈로그를 빌드하는 데 권장됩니다.

기본 스크롤 가능한 목록을 구현하려면 표준 지연 구성요소를 사용하세요. 이러한 구성요소는 D패드 탐색을 자동으로 처리하고 포커스가 있는 항목을 뷰로 가져옵니다.

import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items

@Composable
fun MovieCatalog(movies: List<Movie>) {
    LazyRow {
        items(movies) { movie ->
            MovieCard(
                movie = movie,
                onClick = { /* Handle click */ }
            )
        }
    }
}

BringIntoViewSpec로 스크롤 동작 맞춤설정

디자인에 특정 '피벗' 포인트가 필요한 경우 (예: 포커스가 있는 항목을 왼쪽 가장자리에서 정확히 30% 떨어진 위치에 유지) BringIntoViewSpec를 사용하여 스크롤 동작을 맞춤설정할 수 있습니다. 이를 통해 포커스가 있는 항목을 수용하기 위해 뷰포트가 스크롤되는 방식을 정확하게 정의할 수 있으므로 이전 pivotOffsets 기능을 대체합니다.

1. 맞춤 BringIntoViewSpec 정의

다음 도우미 컴포저블을 사용하면 상위 및 하위 비율을 기반으로 '피벗'을 정의할 수 있습니다. parentFraction는 컨테이너에서 항목이 배치될 위치를 결정하고 childFraction는 해당 지점에 정렬되는 항목의 부분을 결정합니다.

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PositionFocusedItemInLazyLayout(
    parentFraction: Float = 0.3f,
    childFraction: Float = 0f,
    content: @Composable () -> Unit,
) {
    val bringIntoViewSpec = remember(parentFraction, childFraction) {
        object : BringIntoViewSpec {
            override fun calculateScrollDistance(
                offset: Float,       // Item's initial position
                size: Float,         // Item's size
                containerSize: Float // Container's size
            ): Float {
                // Calculate the offset position of the item's leading edge.
                val initialTargetForLeadingEdge =
                    parentFraction * containerSize - (childFraction * size)
                // If the item fits in the container, and scrolling would cause
                // its trailing edge to be clipped, adjust targetForLeadingEdge
                // to prevent over-scrolling near the end of list.
                val targetForLeadingEdge = if (size <= containerSize &&
                    (containerSize - initialTargetForLeadingEdge) < size) {
                    // If clipped, align the item's trailing edge with the
                    // container's trailing edge.
                    containerSize - size
                } else {
                    initialTargetForLeadingEdge
                }
                // Return scroll distance relative to initial item position.
                return offset - targetForLeadingEdge
            }
        }
    }

    // Apply the spec to all scrollables in the hierarchy
    CompositionLocalProvider(
        LocalBringIntoViewSpec provides bringIntoViewSpec,
        content = content,
    )
}

2. 맞춤 사양 적용

도우미로 레이아웃을 래핑하여 위치를 적용합니다. 이는 카탈로그의 여러 행에 걸쳐 '일관된 포커스 라인'을 만드는 데 유용합니다.

PositionFocusedItemInLazyLayout(
    parentFraction = 0.3f, // Pivot 30% from the edge
    childFraction = 0.5f   // Center of the item aligns with the pivot
) {
    LazyColumn {
        items(sectionList) { section ->
            // This row and its items will respect the 30% pivot
            LazyRow { ... }
        }
    }
}

3. 특정 중첩 레이아웃 선택 해제

맞춤 피벗 대신 표준 스크롤 동작을 사용해야 하는 특정 중첩 레이아웃이 있는 경우 DefaultBringIntoViewSpec를 제공합니다.

private val DefaultBringIntoViewSpec = object : BringIntoViewSpec {}

PositionFocusedItemInLazyLayout {
    LazyColumn {
        item {
            // This row will ignore the custom pivot and use default behavior
            CompositionLocalProvider(LocalBringIntoViewSpec provides DefaultBringIntoViewSpec) {
                LazyRow { ... }
            }
        }
    }
}

결과적으로 빈 BringIntoViewSpec를 전달하면 프레임워크의 기본 동작이 인계됩니다.

TV Foundation에서 Compose Foundation으로 이전

androidx.tv.foundation의 TV 전용 지연 레이아웃은 표준 Compose Foundation 레이아웃을 위해 지원 중단됩니다.

종속 항목 업데이트

build.gradle가 다음 항목에 버전 1.7.0 이상을 사용하는지 확인합니다.

  • androidx.compose.foundation
  • androidx.compose.runtime

구성요소 매핑

이전하려면 가져오기를 업데이트하고 구성요소에서 Tv 접두사를 삭제하세요.

지원 중단된 TV 구성요소 Compose Foundation 대체
TvLazyRow LazyRow
TvLazyColumn LazyColumn
TvLazyHorizontalGrid LazyHorizontalGrid
TvLazyVerticalGrid LazyVerticalGrid
pivotOffsets BringIntoViewSpec (LocalBringIntoViewSpec을 통해)