Compose를 사용하여 뷰 기반 Android 앱에 적응형 레이아웃 추가

1. 시작하기 전에

Android 기기는 다양한 모양과 크기로 제공됩니다. 따라서 단일 Android 패키지(APK) 또는 Android App Bundle(AAB)을 사용하는 여러 기기와 사용자가 이용 가능하도록 앱의 레이아웃이 서로 다른 화면 크기에 맞게 조정되도록 해야 합니다. 이렇게 하려면 특정 화면 크기와 가로세로 비율을 취하는 정적 크기로 앱을 정의하는 대신 반응형 레이아웃과 적응형 레이아웃을 사용하여 앱을 정의해야 합니다. 적응형 레이아웃은 사용할 수 있는 화면 공간에 따라 변경됩니다.

이 Codelab에서는 적응형 UI를 빌드하고, 스포츠 목록과 각 스포츠의 세부정보를 표시하는 앱이 대형 화면 기기를 지원하도록 조정하는 방법의 기본사항을 배웁니다. Sports 앱은 홈, 즐겨찾기, 설정의 세 가지 화면으로 구성됩니다. 홈 화면에는 스포츠 목록과 목록에서 스포츠를 선택할 경우 볼 수 있는 뉴스의 자리표시자가 표시됩니다. 즐겨찾기 화면과 설정 화면에도 자리표시자 텍스트가 표시됩니다. 하단 탐색 메뉴에서 연결 항목을 선택하여 화면을 전환할 수 있습니다.

대형 화면에서는 앱에 다음과 같은 레이아웃 문제가 발생합니다.

  • 세로 방향으로 앱을 사용할 수 없습니다.
  • 대형 화면에 빈 공간이 많이 표시됩니다.
  • 대형 화면에는 항상 하단 탐색 메뉴가 표시됩니다.

적응형 앱을 만들면 다음과 같은 장점이 있습니다.

  • 가로 모드 세로 모드 방향을 지원합니다.
  • 가로 공간이 충분하면 스포츠 목록과 각 스포츠의 뉴스를 나란히 표시합니다.
  • Material Design 가이드라인에 따라 탐색 구성요소를 표시합니다.

이 앱은 프래그먼트가 몇 개 있는 단일 활동 앱입니다. 다음 파일로 작업하게 됩니다.

  • AndroidManifest.xml 파일: Sports 앱에 관한 메타데이터를 제공합니다.
  • MainActivity.kt 파일: activity_main.xml 파일로 생성된 코드와 override 주석, 너비 창-클래스 크기를 나타내는 enum 클래스, 앱 창의 너비 창-크기 클래스를 가져오기 위한 메서드 정의가 포함되어 있습니다. 하단 탐색 메뉴는 활동이 만들어질 때 초기화됩니다.
  • activity_main.xml 파일: Main 활동의 기본 레이아웃을 정의합니다.
  • layout-sw600dp/activity_main.xml 파일: Main 활동의 대체 레이아웃을 정의합니다. 대체 레이아웃은 앱의 창 너비가 600dp 값보다 크거나 같을 때 효과적입니다. 콘텐츠는 시작점의 기본 레이아웃과 동일합니다.
  • SportsListFragment.kt 파일: 스포츠-목록 구현과 맞춤 뒤로 탐색이 포함되어 있습니다.
  • fragment_sports_list.xml 파일: 스포츠 목록의 레이아웃을 정의합니다.
  • navigation_menu.xml 파일: 하단 탐색 메뉴 항목을 정의합니다.

소형 창에서 Sports 앱은 스포츠 목록을 탐색 메뉴와 함께 상단 탐색 구성요소로 표시합니다. 보통 크기의 창에서 Sports 앱은 스포츠 목록과 스포츠 뉴스를 나란히 표시합니다. 탐색 레일은 상단 탐색 구성요소로 표시됩니다. 펼침 크기의 창에서 Sports 앱은 탐색 창과 스포츠 목록, 뉴스를 표시합니다.

그림 1. 단일 APK 또는 AAB를 사용하는 Sports 앱은 다양한 창 크기를 지원합니다.

기본 요건

학습할 내용

  • 구성 변경을 지원하는 방법
  • 코드를 거의 수정하지 않고 대체 레이아웃을 추가하는 방법
  • 다양한 창 크기에 따라 다르게 동작하는 목록 세부정보 UI를 구현하는 방법

빌드할 항목

다음을 지원하는 Android 앱

  • 가로 모드 기기 방향
  • 태블릿, 데스크톱 및 휴대기기
  • 다양한 화면 크기의 목록 세부정보 동작

필요한 항목

2. 설정

다음과 같이 이 Codelab의 코드를 다운로드하고 프로젝트를 설정합니다.

  1. 명령줄에서 이 GitHub 저장소의 코드를 클론합니다.
$ git clone https://github.com/android/add-adaptive-layouts
  1. Android 스튜디오에서 AddingAdaptiveLayout 프로젝트를 엽니다. 프로젝트는 여러 git 브랜치로 빌드됩니다.
  • main 브랜치에는 이 프로젝트의 시작 코드가 포함되어 있습니다. 이 브랜치를 변경하여 Codelab을 완료하게 됩니다.
  • end 브랜치에는 이 Codelab의 솔루션이 포함되어 있습니다.

3. 상단 탐색 구성요소를 Compose로 이전

Sports 앱은 하단 탐색 메뉴를 상단 탐색 구성요소로 사용합니다. 이 탐색 구성요소는 BottomNavigationView 클래스로 구현됩니다. 이 섹션에서는 상단 탐색 구성요소를 Compose로 이전합니다.

연결된 메뉴 리소스의 콘텐츠를 봉인 클래스로 표현

  1. navigation_menu.xml 파일의 콘텐츠를 표시하기 위한 봉인 MenuItem 클래스를 만들고 이를 iconId 매개변수, labelId 매개변수, destinationId 매개변수에 전달합니다.
  2. Home 객체, Favorite 객체, Settings 객체를 대상에 해당하는 서브클래스로 추가합니다.
sealed class MenuItem(
    // Resource ID of the icon for the menu item
    @DrawableRes val iconId: Int,
    // Resource ID of the label text for the menu item
    @StringRes val labelId: Int,
    // ID of a destination to navigate users
    @IdRes val destinationId: Int
) {

    object Home: MenuItem(
        R.drawable.ic_baseline_home_24,
        R.string.home,
        R.id.SportsListFragment
    )

    object Favorites: MenuItem(
        R.drawable.ic_baseline_favorite_24,
        R.string.favorites,
        R.id.FavoritesFragment
    )

    object Settings: MenuItem(
        R.drawable.ic_baseline_settings_24,
        R.string.settings,
        R.id.SettingsFragment
    )
}

하단 탐색 메뉴를 구성 가능한 함수로 구현

  1. 세 가지 매개변수를 사용하는 구성 가능한 BottomNavigationBar 함수를 정의합니다. 세 가지 매개변수에 해당하는 객체는 List<MenuItem> 값으로 설정되는 menuItems 객체와 Modifier = Modifier 값으로 설정되는 modifier 객체, (MenuItem) -> Unit = {} 람다 함수로 설정되는 onMenuSelected 객체입니다.
  2. 구성 가능한 BottomNavigationBar 함수의 본문에서 modifier 매개변수를 사용하는 NavigationBar() 함수를 호출합니다.
  3. NavigationBar() 함수에 전달된 람다 함수에서 menuItems 매개변수에 관해 forEach() 메서드를 호출한 다음, foreach() 메서드 호출에 설정된 람다 함수에서 NavigationBarItem() 함수를 호출합니다.
  4. NavigationBarItem() 함수를 false 값으로 설정된 selected 매개변수에 전달합니다. onClick 매개변수는 MenuItem 매개변수를 갖는 onMenuSelected 함수가 포함된 람다 함수로 설정되고, icon 매개변수는 painter = painterResource(id = menuItem.iconId) 매개변수와 contentDescription = null 매개변수를 취하는 Icon 함수가 포함된 람다 함수로 설정되며, label 매개변수는 (text = stringResource(id = menuItem.labelId) 매개변수를 취하는 Text 함수가 포함된 람다 함수로 설정되었습니다.
@Composable
fun BottomNavigationBar(
    menuItems: List<MenuItem>,
    modifier: Modifier = Modifier,
    onMenuSelected: (MenuItem) -> Unit = {}
) {

    NavigationBar(modifier = modifier) {
        menuItems.forEach { menuItem ->
            NavigationBarItem(
                selected = false,
                onClick = { onMenuSelected(menuItem) },
                icon = {
                    Icon(
                        painter = painterResource(id = menuItem.iconId),
                        contentDescription = null)
                },
                label = { Text(text = stringResource(id = menuItem.labelId))}
            )
        }
    }
}

BottomNavigationView 요소를 레이아웃 리소스 파일의 ComposeView 요소로 대체

  • activity_main.xml 파일에서 BottomNavigationView 요소를 ComposeView 요소로 바꿉니다. 이렇게 하면 Compose에서 하단 탐색 구성요소가 뷰 기반 UI 레이아웃에 삽입됩니다.

activity_main.xml

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment_content_main"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toTopOf="@id/top_navigation"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/nav_graph" />

        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/navigation"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:menu="@menu/top_navigation" />
    </androidx.constraintlayout.widget.ConstraintLayout>

Compose 기반의 하단 탐색 메뉴를 뷰 기반 UI 레이아웃과 통합

  1. MainActivity.kt 파일에서 MenuItem 객체 목록으로 설정된 navigationMenuItems 변수를 정의합니다. MenuItems 객체는 하단 탐색 메뉴에 목록 순서대로 표시됩니다.
  2. BottomNavigationBar() 함수를 호출하여 ComposeView 객체에 하단 탐색 메뉴를 삽입합니다.
  3. BottomNavigationBar 함수에 전달된 콜백 함수에서 사용자가 선택한 항목과 연결된 대상으로 이동합니다.

MainActivity.kt

val navigationMenuItems = listOf(
    MenuItem.Home,
    MenuItem.Favorites,
    MenuItem.Settings
)

binding.navigation.setContent {
    MaterialTheme {
        BottomNavigationBar(menuItems = navigationMenuItems){ menuItem ->
            navController.navigate(screen.destinationId)
        }
    }
}

4. 가로 방향 지원

앱이 대형 화면 기기를 지원하면 가로 방향과 세로 방향을 지원해야 합니다. 현재 앱에는 MainActivity 활동 한 가지만 있습니다.

기기에서 활동의 디스플레이 방향은 AndroidManifest.xml 파일에 android:screenOrientation 속성(portrait 값으로 설정됨)과 함께 설정됩니다.

가로 방향을 지원하도록 앱을 만듭니다.

  1. android:screenOrientation 속성을 fullUser 값으로 설정합니다. 이 구성을 사용하면 사용자가 화면 방향을 잠글 수 있습니다. 기기 방향 센서가 4개 방향 중에서 결정합니다.

AndroidManifest.xml

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:screenOrientation="fullUser">

Sports 앱은 AndroidManifest.xml 파일에서 fullUser 값을 활동 요소의 android:screenOrientation 속성으로 설정하여 가로 방향을 지원합니다.

그림 2. AndroidManifest.xml 파일을 업데이트하면 앱이 가로 방향으로 실행됩니다.

5. 창 크기 클래스

앱에 사용할 수 있는 원시 창 크기를 사용하여, 창 크기를 사전 정의된 크기 클래스(작게, 보통, 펼침)로 분류하는 데 도움이 되는 중단점 값입니다. 적응형 레이아웃을 디자인, 개발, 테스트할 때 이러한 크기 클래스를 사용할 수 있습니다.

사용 가능한 너비와 높이는 개별적으로 파티션이 나뉘므로 언제라도 앱에는 두 가지 창 크기 클래스(너비 창 크기 클래스, 높이 창 크기 클래스)가 연결되어 있습니다.

세 개의 너비 창 크기 클래스 간에는 두 개의 중단점이 있습니다. 600dp 값은 작게와 보통 간의 중단점이고, 840dp 값은 보통과 펼침 너비 창 크기 클래스 간의 중단점입니다.

그림 3. 너비 창 크기 클래스와 연결된 중단점

세 개의 높이 창 크기 클래스에는 두 개의 중단점이 있습니다. 480dp 값은 작게와 보통 높이 창 크기 클래스 간의 중단점이고, 900dp 값은 보통과 펼침 높이 창 크기 클래스 간의 중단점입니다.

그림 4. 높이 창 크기 클래스와 연결된 중단점

창 크기 클래스는 앱의 현재 창 크기를 나타냅니다. 즉, 실제 기기 크기로 창 크기 클래스를 결정할 수 없습니다. 앱이 같은 기기에서 실행되더라도 연결된 창 크기 클래스는 구성에 따라 변경됩니다(예: 화면 분할 모드에서 앱을 실행하는 경우). 이에 따른 중요한 두 가지 결과가 있습니다.

  • 실제 기기는 특정 창 크기 클래스를 보장하지 않습니다.
  • 창 크기 클래스는 앱의 전체 기간 동안 변경될 수 있습니다.

앱에 적응형 레이아웃을 추가한 후 작게, 보통, 펼침 창 크기 클래스를 대상으로 모든 범위의 창 크기에서 앱을 테스트합니다. 창 크기 클래스에 관한 테스트가 각각 필요하지만, 대부분의 경우 이것만으로는 충분하지 않습니다. 다양한 화면 크기로 앱을 테스트하여 UI 크기 조정이 제대로 이루어지도록 하는 것이 중요합니다. 자세한 내용은 대형 화면 레이아웃대형 화면 앱 품질을 참고하세요.

6. 대형 화면에 목록 창과 세부정보 창을 나란히 배치

목록 세부정보 UI는 현재 너비 창 크기 클래스에 따라 다르게 작동해야 할 수 있습니다. 보통 또는 펼침 너비 창 크기 클래스가 앱과 연결되면 앱에 목록 창과 세부정보 창을 나란히 표시하기에 충분한 공간이 있다는 의미입니다. 따라서 사용자는 화면 전환 없이 항목 목록과 선택한 항목의 세부정보를 볼 수 있습니다. 하지만 작은 화면에서는 이렇게 하면 너무 복잡할 수 있습니다. 작은 화면에서는 처음에는 목록 창을 표시하고 한 번에 창 하나씩 표시하는 것이 더 나을 수 있습니다. 세부정보 창에는 사용자가 목록에서 항목을 탭할 때 선택한 항목의 세부정보가 표시됩니다. SlidingPaneLayout 클래스는 두 사용자 환경 중에서 현재 창 크기에 적절한 환경을 결정하는 로직을 관리합니다.

목록 창 레이아웃 구성

SlidingPaneLayout 클래스는 뷰 기반 UI 구성요소입니다. 이 Codelab에서는 목록 창의 레이아웃 리소스 파일인 fragment_sports_list.xml 파일을 수정합니다.

SlidingPaneLayout 클래스는 두 개의 하위 요소를 사용합니다. 각 하위 요소의 너비와 가중치 속성은 창이 두 뷰를 나란히 표시할 만큼 큰지 확인하는 SlidingPaneLayout 클래스의 핵심 요소입니다. 그만큼 크지 않으면 전체 화면 목록이 전체 화면 세부정보 UI로 대체됩니다. 창 크기가 창을 나란히 표시하기 위한 최소 요건보다 크면 가중치 값이 참조되어 두 창의 크기가 비례해서 조정됩니다.

SlidingPaneLayout 클래스가 fragment_sports_list.xml 파일에 적용되었습니다. 목록 창의 너비는 1280dp가 되도록 구성됩니다. 이는 목록 창과 세부정보 창이 나란히 표시되지 않는 이유입니다.

화면 너비가 580dp보다 클 경우 창이 나란히 표시되도록 다음과 같이 구성합니다.

  • RecyclerView280dp 너비로 설정합니다.

fragment_sports_list.xml

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SportsListFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:clipToPadding="false"
        android:padding="8dp"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

    <androidx.fragment.app.FragmentContainerView
        android:layout_height="match_parent"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:id="@+id/detail_container"
        android:name="com.example.android.sports.NewsDetailsFragment"/>
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

sports_list_fragment.xml 파일을 업데이트하면 Sports 앱은 스포츠 목록과 선택한 스포츠의 뉴스를 나란히 표시합니다.

그림 5. 레이아웃 리소스 파일을 업데이트하면 목록 창과 세부정보 창이 나란히 표시됩니다.

세부정보 창 변경

이제 보통 또는 펼침 너비 창 크기 클래스가 앱과 연결되면 목록 창과 세부정보 창이 나란히 표시됩니다. 하지만 사용자가 목록 창에서 항목을 선택하면 화면은 세부정보 창으로 완전히 전환됩니다.

앱에는 스포츠 목록과 뉴스가 계속 나란히 표시되어야 하지만, 화면 전환이 실행되면 스포츠 뉴스만 표시됩니다.

그림 6. 목록에서 스포츠를 선택하면 화면이 세부정보 창으로 전환됩니다.

이 문제는 사용자가 목록 창에서 항목을 선택할 때 탐색이 트리거되어 발생합니다. 관련 코드는 SportsListFragment.kt 파일에서 확인할 수 있습니다.

SportsListFragment.kt

val adapter = SportsAdapter {
    sportsViewModel.updateCurrentSport(it)
    // Navigate to the details pane.
    val action =
       SportsListFragmentDirections.actionSportsListFragmentToNewsFragment()
    this.findNavController().navigate(action)
}

목록 창과 세부정보 창을 나란히 표시할 공간이 충분하지 않은 경우에만 화면이 세부정보 창으로 완전히 전환되는지 확인합니다.

  • adapter 함수 변수에서 SlidingPaneLayout 클래스의 isSlidable 속성이 true이고 SlidingPaneLayout 클래스의 isOpen 속성이 false인지 확인하는 if 문을 추가합니다.

SportsListFragment.kt

val adapter = SportsAdapter {
    sportsViewModel.updateCurrentSport(it)
    if(slidingPaneLayout.isSlidable && !slidingPaneLayout.isOpen){
        // Navigate to the details pane.
        val action =
           SportsListFragmentDirections.actionSportsListFragmentToNewsFragment()
        this.findNavController().navigate(action)
    }
}

7. 너비 창 크기 클래스를 기준으로 올바른 탐색 구성요소 선택

Material Design에서는 앱이 상황에 따라 유연하게 구성요소를 선택하는 방식이 좋습니다. 이 섹션에서는 현재 너비 창 크기 클래스에 따라 상단 탐색 메뉴의 탐색 구성요소를 선택합니다. 다음 표에는 각 창 크기 클래스에 필요한 탐색 구성요소가 설명되어 있습니다.

너비 창 크기 클래스

탐색 구성요소

작게

하단 탐색

보통

탐색 레일

펼침

영구 탐색 창

탐색 레일 구현

  1. 세 가지 매개변수를 사용하는 구성 가능한 NavRail() 함수를 만듭니다. 그 세 가지 매개변수는 List<MenuItem> 값으로 설정되는 menuItems 객체와 Modifier 값으로 설정되는 modifier 객체, 그리고 onMenuSelected 람다 함수입니다.
  2. 함수 본문에서 modifier 객체를 매개변수로 사용하는 NavigationRail() 함수를 호출합니다.
  3. BottomNavigationBar() 함수에 NavigationBarItem 함수를 사용하여 한 것처럼, menuItems 객체의 각 MenuItem 객체에 관해 NavigationRailItem() 함수를 호출합니다.

영구 탐색 창 구현

  1. 세 가지 매개변수를 사용하는 구성 가능한 NavigationDrawer() 함수를 만듭니다. 그 세 가지 매개변수는 List<MenuItem> 값으로 설정되는 menuItems 객체와 Modifier 값으로 설정되는 modifier 객체, 그리고 onMenuSelected 람다 함수입니다.
  2. 함수 본문에서 modifier 객체를 매개변수로 사용하는 Column() 함수를 호출합니다.
  3. menuItems 객체의 각 MenuItem 객체에 관해 Row() 함수를 호출합니다.
  4. Row() 함수의 본문에서 BottomNavigationBar() 함수에 NavigationBarItem 함수를 사용하여 한 것처럼, icontext 라벨을 추가합니다.

너비 창 크기 클래스를 기준으로 올바른 탐색 구성요소 선택

  1. 현재 너비 창 크기 클래스를 가져오려면, 구성 가능한 함수 내에서 ComposeView 객체의 setContent() 메서드에 전달된 rememberWidthSizeClass() 함수를 호출합니다.
  2. 가져온 너비 창 크기 클래스에 따라 탐색 구성요소를 선택하고 선택한 탐색 구성요소를 호출하기 위한 조건부 브랜치를 만듭니다.
  3. Modifier 객체를 NavigationDrawer 함수에 전달하여 너비 값을 256dp로 지정합니다.

ActivityMain.kt:

binding.navigation.setContent {
    MaterialTheme {
        when(rememberWidthSizeClass()){
            WidthSizeClass.COMPACT ->
                BottomNavigationBar(menuItems = navigationMenuItems){ menuItem ->
                    navController.navigate(screen.destinationId)
                }
            WidthSizeClass.MEDIUM ->
                NavRail(menuItems = navigationMenuItems){ menuItem ->
                    navController.navigate(screen.destinationId)
                }
            WidthSizeClass.EXPANDED ->
                NavigationDrawer(
                    menuItems = navigationMenuItems,
                    modifier = Modifier.width(256.dp)
                ) { menuItem ->
                    navController.navigate(screen.destinationId)
                }
        }

    }
}

탐색 구성요소를 적절한 위치에 배치

이제 앱은 현재 너비 창 크기 클래스에 따라 올바른 탐색 구성요소를 선택할 수 있지만 선택한 구성요소가 예상대로 배치되지는 않습니다. ComposeView 요소가 FragmentViewContainer 요소 아래에 배치되기 때문입니다.

MainActivity 클래스의 대체 레이아웃 리소스를 업데이트합니다.

  1. layout-sw600dp/activity_main.xml 파일을 엽니다.
  2. ComposeView 요소와 FragmentContainer 요소를 가로로 배치하도록 제약 조건을 업데이트합니다.

layout-sw600dp/activity_main.xml

  <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment_content_main"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintLeft_toRightOf="@+id/top_navigation"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:navGraph="@navigation/nav_graph" />

        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/top_navigation"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:menu="@menu/top_navigation" />
    </androidx.constraintlayout.widget.ConstraintLayout>

수정 후 앱은 연결된 너비 창 크기 클래스에 따라 올바른 탐색 구성요소를 선택합니다. 첫 번째 스크린샷은 보통 너비 창 크기 클래스의 화면입니다. 두 번째 스크린샷은 펼침 너비 창 크기 클래스의 화면입니다.

Sports 앱은 보통 너비 창 크기 클래스와 연결된 경우 탐색 레일, 스포츠 목록, 뉴스를 표시합니다. Sports 앱은 앱이 펼침 너비 창 크기 클래스와 연결된 경우 탐색 창, 스포츠 목록, 뉴스를 홈 화면에 표시합니다.

그림 7. 보통 너비와 펼침 너비 창 크기 클래스의 화면.

8. 축하합니다

축하합니다. 이 Codelab을 완료하고 Compose를 사용하여 뷰 기반 Android 앱에 적응형 레이아웃을 추가하는 방법을 알아봤습니다. 그 과정에서 SlidingPaneLayout 클래스와 창 크기 클래스, 그리고 너비 창 크기 클래스에 따라 선택되는 탐색 구성요소도 알아봤습니다.

자세히 알아보기