그리드 및 mediaQuery가 있는 테이블탑 자세 레이아웃

별표 4개 평점 아이콘

폴더블 기기의 탁자 모드는 단일 화면을 물리적 접힘 (또는 힌지)으로 구분된 두 개의 기능적 디스플레이 영역으로 분할합니다. 탁자 모드를 사용하면 앱의 기본 콘텐츠 (예: 동영상 플레이어 또는 게임 캔버스)를 상단 절반에 배치하고 대화형 요소 (예: 미디어 컨트롤 또는 컨트롤 패드)를 하단 절반에 배치할 수 있습니다.

테이블탑 자세의 폴더블 기기에서 상단 화면에는 동영상 콘텐츠가 표시되고 하단 화면에는 재생 컨트롤이 표시됩니다.
그림 1. 탁자 모드는 콘텐츠와 대화형 컨트롤을 물리적 접힘으로 분할합니다.

권장사항

앱에 탁자 모드 지원을 추가하려면 Grid API를 사용하여 행 2개와 열 1개로 레이아웃을 정의하고 탁자 모드 창 자세 미디어 쿼리를 적용합니다.

  • 탁자 모드를 반응형으로 감지: mediaQuery 컴포저블을 사용하여 기기 상태를 관찰합니다. 기기가 탁자 모드로 접히면 (windowPosture == Posture.Tabletop) 활동을 다시 시작하거나 복잡한 수명 주기 처리를 하지 않고도 레이아웃을 동적으로 전환합니다.
  • Grid로 레이아웃 구성: 탁자 모드에서는 Grid API (2 rows, 1 column)를 사용하여 기본 시각적 콘텐츠를 접힘 위의 상단 행으로, 대화형 컨트롤을 접힘 아래의 하단 행으로 깔끔하게 분리합니다.
  • 오버레이에 암시적 z-색인 활용: 표준 (탁자 모드 아님) 자세에서는 동일한 셀에 여러 영역 ID를 할당하여 플레이어 위에 컨트롤을 오버레이합니다. Grid는 선언 순서대로 항목을 앞뒤로 자동으로 렌더링합니다.
  • 원활한 전환 제공: 표준 전체 화면 레이아웃과 탁자 모드 간에 전환할 때 상태와 스크롤 위치가 유지되도록 합니다.

재료

  • Grid: 구성 가능한 행과 열에 항목을 배치할 수 있는 적응형 레이아웃 컨테이너로, 서로 다른 화면 영역에 콘텐츠를 분할하는 데 적합합니다.
  • mediaQuery: 컴포저블 내에서 앱의 현재 창 자세, 크기 클래스, 하드웨어 특성을 직접 쿼리하는 데 사용되는 Jetpack Compose API입니다.

단계

mediaQuery()를 사용하여 기기 자세를 쿼리합니다. 기기가 탁자 모드일 때는 앱의 콘텐츠를 행 2개와 열 1개가 있는 세로 그리드로 정렬하여 기본 콘텐츠가 상단에 유지되고 컨트롤이 하단에서 계속 액세스할 수 있도록 합니다.

1. 탁자 모드의 그리드 구성 정의

컴포저블 함수 내에서 enum을 사용하여 레이아웃 영역 ID를 정의하고 기본 자세와 탁자 모드 자세에 GridConfigurationScope 블록을 설정합니다.

defaultConfig에서 두 영역이 모두 동일한 셀 (row = 1, column = 1)에 할당됩니다. Grid는 레이아웃에서 나중에 선언된 컴포저블이 이전 컴포저블 위에 렌더링되는 암시적 z-색인을 사용하므로 컨트롤러가 추가 컨테이너 중첩 없이 플레이어를 오버레이할 수 있습니다 (자세한 내용은 그리드를 사용한 UI 요소 오버레이 참고).

enum class VideoPlayerArea { Player, Controller }

object PlayerGridConfig {
    // Default: Single area layout with overlay (controls on top of player)
    val defaultConfig: GridConfigurationScope.() -> Unit = {
        row(1f)
        column(1f)
        area(areaId = VideoPlayerArea.Player, row = 1, column = 1)
        area(areaId = VideoPlayerArea.Controller, row = 1, column = 1)
    }

    // Tabletop: Two rows splitting content across the fold
    val tabletopConfig: GridConfigurationScope.() -> Unit = {
        row(0.5f)
        row(0.5f)
        column(1f)
        area(areaId = VideoPlayerArea.Player, row = 1, column = 1) // Top half above fold
        area(areaId = VideoPlayerArea.Controller, row = 2, column = 1) // Bottom half below fold
    }
}

2. 탁자 모드 감지 및 그리드에 구성 적용

mediaQuery를 사용하여 windowPosture를 기반으로 활성 구성을 선택하고 config를 Grid에 전달하고 컴포저블 콘텐츠를 영역 ID에 바인딩합니다. VideoPlayerArea.Controller 전에 VideoPlayerArea.Player를 선언하면 둘 다 defaultConfig에서 셀을 공유할 때 컨트롤이 플레이어 위에 렌더링됩니다.

val config = mediaQuery {
    when (windowPosture) {
        Posture.Tabletop -> PlayerGridConfig.tabletopConfig
        else -> PlayerGridConfig.defaultConfig
    }
}

Grid(config = config) {
    Box(
        modifier = Modifier
            .gridItem(areaId = VideoPlayerArea.Player)
    ) {
        // VideoPlayerContent
    }

    Box(
        modifier = Modifier
            .gridItem(areaId = VideoPlayerArea.Controller)
    ) {
        // PlaybackControlsContent
    }
}

결과

mediaQuery와 Grid를 결합하면 앱이 폴더블 기기가 탁자 모드로 부분적으로 접힐 때를 동적으로 감지하고 접힘으로 구분된 두 개의 기능적 디스플레이 영역으로 UI를 자동으로 조정합니다.

추가 리소스