지원 창 레이아웃은 관련 지원 정보를 표시하면서 사용자가 앱의 기본 콘텐츠에 집중할 수 있도록 합니다. 예를 들어 기본 창에는 영화에 관한 세부정보가 표시되고 지원 창에는 비슷한 영화, 동일한 감독의 영화 또는 동일한 배우가 출연하는 작품이 표시될 수 있습니다.
자세한 내용은 Material 3 지원 창 가이드라인을 참고하세요.
스캐폴드로 지원 창 구현
NavigableSupportingPaneScaffold는 Jetpack Compose에서 지원 창 레이아웃을 구현하는 작업을 간소화하는 컴포저블입니다. SupportingPaneScaffold을 래핑하고 내장 탐색 및 뒤로 탐색 예측 처리를 추가합니다.
지원 창 스캐폴드는 최대 3개의 창을 지원합니다.
- 기본 창: 기본 콘텐츠를 표시합니다.
- 지원 창: 기본 창과 관련된 추가 컨텍스트 또는 도구를 제공합니다.
- 추가 창 (선택사항): 필요할 때 보조 콘텐츠에 사용됩니다.
스캐폴드는 창 크기에 따라 적응합니다.
- 큰 창에서는 기본 창과 지원 창이 나란히 표시됩니다.
작은 창에서는 한 번에 하나의 창만 표시되며 사용자가 탐색할 때 전환됩니다.
그림 1. 지원 창 레이아웃
종속 항목 추가
NavigableSupportingPaneScaffold은 Material 3 적응형 레이아웃 라이브러리의 일부입니다.
다음 세 개의 관련 종속 항목을 앱 또는 모듈의 build.gradle 파일에 추가합니다.
Kotlin
implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")
Groovy
implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
adaptive-layout:
ListDetailPaneScaffold,SupportingPaneScaffold과 같은 적응형 레이아웃adaptive-navigation: 창 내 및 창 간 탐색을 위한 컴포저블과 기본적으로 탐색을 지원하는 적응형 레이아웃(예:
NavigableListDetailPaneScaffold,NavigableSupportingPaneScaffold)
프로젝트에 compose-material3-adaptive 버전 1.1.0-beta1 이상이 포함되어 있는지 확인합니다.
뒤로 탐색 예측 동작 선택
Android 15 이하에서 뒤로 탐색 예측 애니메이션을 사용 설정하려면 뒤로 탐색 예측 동작을 지원하도록 선택해야 합니다. 선택하려면 AndroidManifest.xml 파일 내의 <application> 태그 또는 개별 <activity> 태그에 android:enableOnBackInvokedCallback="true"를 추가합니다.
앱이 Android 16 (API 수준 36) 이상을 타겟팅하면 기본적으로 뒤로 탐색 예측이 사용 설정됩니다.
탐색기 만들기
작은 창에서는 한 번에 하나의 창만 표시되므로 ThreePaneScaffoldNavigator를 사용하여 창 간에 이동합니다. rememberSupportingPaneScaffoldNavigator를 사용하여 탐색기 인스턴스를 만듭니다.
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope()
스캐폴드에 탐색기 전달
스캐폴드에는 스캐폴드의 상태를 나타내는 인터페이스인 ThreePaneScaffoldNavigator와 ThreePaneScaffoldValue, PaneScaffoldDirective가 필요합니다.
NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { /*...*/ }, supportingPane = { /*...*/ }, )
기본 창과 지원 창은 콘텐츠가 포함된 컴포저블입니다. 탐색 중에 기본 창 애니메이션을 적용하려면 AnimatedPane를 사용합니다. 스캐폴드 값을 사용하여 지원 창이 숨겨져 있는지 확인합니다. 숨겨져 있다면 navigateTo(SupportingPaneScaffoldRole.Supporting)를 호출하여 지원 창을 표시하는 버튼을 표시합니다.
큰 화면의 경우 BackNavigationBehavior.PopUntilScaffoldValueChange 상수를 전달하여 ThreePaneScaffoldNavigator.navigateBack() 메서드를 사용하여 지원 창을 닫습니다. 이 메서드를 호출하면 NavigableSupportingPaneScaffold가 강제로 재구성됩니다.
리컴포지션 중에 ThreePaneScaffoldNavigator.currentDestination 속성을 확인하여 지원 창을 표시할지 결정합니다.
다음은 스캐폴드의 전체 구현입니다.
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() val backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { AnimatedPane( modifier = Modifier .safeContentPadding() .background(Color.Red) ) { if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) { Button( modifier = Modifier .wrapContentSize(), onClick = { scope.launch { scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting) } } ) { Text("Show supporting pane") } } else { Text("Supporting pane is shown") } } }, supportingPane = { AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = Modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } } )
창 컴포저블 추출
SupportingPaneScaffold의 개별 창을 자체 컴포저블로 추출하여 재사용 가능하고 테스트 가능하도록 합니다. 기본 애니메이션을 원하는 경우 ThreePaneScaffoldScope를 사용하여 AnimatedPane에 액세스합니다.
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun ThreePaneScaffoldPaneScope.MainPane( shouldShowSupportingPaneButton: Boolean, onNavigateToSupportingPane: () -> Unit, modifier: Modifier = Modifier, ) { AnimatedPane( modifier = modifier.safeContentPadding() ) { // Main pane content if (shouldShowSupportingPaneButton) { Button(onClick = onNavigateToSupportingPane) { Text("Show supporting pane") } } else { Text("Supporting pane is shown") } } } @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun ThreePaneScaffoldPaneScope.SupportingPane( scaffoldNavigator: ThreePaneScaffoldNavigator<Any>, modifier: Modifier = Modifier, backNavigationBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange, ) { val scope = rememberCoroutineScope() AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } }
창을 컴포저블로 추출하면 SupportingPaneScaffold 사용이 간소화됩니다 (이전 섹션의 스캐폴드 전체 구현과 비교).
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { MainPane( shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden, onNavigateToSupportingPane = { scope.launch { scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary) } } ) }, supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) }, )
스캐폴드의 특정 측면을 더 세부적으로 제어해야 하는 경우 NavigableSupportingPaneScaffold 대신 SupportingPaneScaffold를 사용하는 것이 좋습니다. 이 함수는 PaneScaffoldDirective 및 ThreePaneScaffoldValue 또는 ThreePaneScaffoldState을 별도로 허용합니다. 이 유연성을 통해 창 간격에 관한 맞춤 로직을 구현하고 동시에 표시할 창 수를 결정할 수 있습니다. ThreePaneScaffoldPredictiveBackHandler를 추가하여 뒤로 탐색 예측 지원을 사용 설정할 수도 있습니다.
ThreePaneScaffoldPredictiveBackHandler 추가
스캐폴드 탐색기 인스턴스를 사용하는 뒤로 탐색 예측 핸들러를 연결하고 backBehavior를 지정합니다. 이는 뒤로 탐색 중에 백 스택에서 대상이 팝되는 방식을 결정합니다. 그런 다음 scaffoldDirective와 scaffoldState을 SupportingPaneScaffold에 전달합니다. ThreePaneScaffoldState를 허용하는 오버로드를 사용하여 scaffoldNavigator.scaffoldState을 전달합니다.
SupportingPaneScaffold 내에서 기본 창과 지원 창을 정의합니다. 기본 창 애니메이션에는 AnimatedPane를 사용합니다.
이 단계를 구현하면 코드가 다음과 같아집니다.
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() ThreePaneScaffoldPredictiveBackHandler( navigator = scaffoldNavigator, backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange ) SupportingPaneScaffold( directive = scaffoldNavigator.scaffoldDirective, scaffoldState = scaffoldNavigator.scaffoldState, mainPane = { MainPane( shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden, onNavigateToSupportingPane = { scope.launch { scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary) } } ) }, supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) }, )