폴더블 기기의 탁자 모드는 단일 화면을 물리적 접힘 (또는 힌지)으로 구분된 두 개의 기능적 디스플레이 영역으로 분할합니다. 탁자 모드를 사용하면 앱의 기본 콘텐츠 (예: 동영상 플레이어 또는 게임 캔버스)를 상단 절반에 배치하고 대화형 요소 (예: 미디어 컨트롤 또는 컨트롤 패드)를 하단 절반에 배치할 수 있습니다.
권장사항
앱에 탁자 모드 지원을 추가하려면 Grid API를 사용하여 행 2개와 열 1개로 레이아웃을 정의하고 탁자 모드 창 자세 미디어 쿼리를 적용합니다.
- 탁자 모드를 반응형으로 감지:
mediaQuery컴포저블을 사용하여 기기 상태를 관찰합니다. 기기가 탁자 모드로 접히면 (windowPosture == Posture.Tabletop) 활동을 다시 시작하거나 복잡한 수명 주기 처리를 하지 않고도 레이아웃을 동적으로 전환합니다. Grid로 레이아웃 구성: 탁자 모드에서는GridAPI (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를 자동으로 조정합니다.