Thêm bố cục thích ứng vào ứng dụng Android dựa trên chế độ xem bằng Compose

1. Trước khi bắt đầu

Các thiết bị Android có nhiều hình dạng và kích thước. Do đó, bạn cần thiết lập bố cục của ứng dụng theo kích thước màn hình để cung cấp cho mọi người dùng và thiết bị thông qua một Gói Android (APK) hoặc Android App Bundle (AAB). Để thực thi mục tiêu đó, bạn cần xác định ứng dụng bằng bố cục thích ứng và phản hồi, thay vì xác định ứng dụng bằng các kích thước tĩnh giả định kích thước màn hình và tỷ lệ khung hình nhất định. Bố cục thích ứng thay đổi dựa trên không gian màn hình có sẵn.

Lớp học lập trình này hướng dẫn cho bạn những nội dung cơ bản về cách tạo giao diện người dùng thích ứng, đồng thời điều chỉnh ứng dụng để hiện danh sách và thông tin chi tiết các môn thể thao để hỗ trợ các thiết bị có màn hình lớn. Ứng dụng thể thao gồm ba màn hình: trang chính, kênh yêu thích và chế độ cài đặt. Trên màn hình chính, bạn sẽ thấy danh sách các môn thể thao và một phần giữ chỗ cho tin tức khi bạn chọn một môn thể thao từ danh sách. Màn hình mục yêu thích và cài đặt cũng hiển thị phần giữ chỗ cho văn bản. Bạn sẽ chọn mục liên kết trong trình đơn điều hướng dưới cùng để chuyển đổi giữa các màn hình.

Ứng dụng bắt đầu với những vấn đề về bố cục trên màn hình lớn:

  • Bạn không thể dùng chế độ này theo hướng dọc.
  • Nó hiển thị quá nhiều không gian trống trên màn hình lớn.
  • Nó luôn hiển thị trình đơn điều hướng dưới cùng trên màn hình lớn.

Bạn làm cho ứng dụng trở nên thích ứng để:

  • Hỗ trợ cả hướng ngang hướng dọc.
  • Hiển thị danh sách các môn thể thao và tin tức về từng môn thể thao cạnh nhau khi có đủ không gian hàng ngang để thực hiện.
  • Hiển thị thành phần điều hướng theo nguyên tắc của Material Design.

Đây là ứng dụng một hoạt động duy nhất có một số mảnh. Bạn sẽ làm việc với các tệp sau:

  • Tệp AndroidManifest.xml cung cấp siêu dữ liệu về ứng dụng thể thao.
  • Tệp MainActivity.kt, chứa mã được tạo bằng tệp activity_main.xml, chú thích override, lớp enum đại diện cho kích thước lớp chiều rộng và định nghĩa phương thức để truy xuất chiều rộng lớp kích thước cửa sổ của cửa sổ ứng dụng. Trình đơn điều hướng dưới cùng sẽ được khởi chạy khi tạo hoạt động.
  • Tệp activity_main.xml xác định bố cục mặc định của hoạt động Main.
  • Tệp layout-sw600dp/activity_main.xml xác định bố cục thay thế cho hoạt động Main. Bố cục thay thế có hiệu lực khi chiều rộng cửa sổ của ứng dụng lớn hơn hoặc bằng giá trị 600dp. Nội dung giống với bố cục mặc định tại điểm bắt đầu.
  • Tệp SportsListFragment.kt chứa nội dung triển khai danh sách các môn thể thao và điều hướng quay lại tuỳ chỉnh.
  • Tệp fragment_sports_list.xml xác định bố cục của danh sách các môn thể thao.
  • Tệp navigation_menu.xml xác định các mục trong trình đơn điều hướng ở dưới cùng.

Ứng dụng thể thao hiện danh sách các môn thể thao trong một cửa sổ thu gọn, trong đó thanh điều hướng là một thành phần điều hướng trên cùng. Ứng dụng thể thao hiển thị danh sách các môn thể thao và tin tức thể thao cạnh nhau trong một cửa sổ trung bình. Thanh điều hướng hiển thị dưới dạng thành phần điều hướng trên cùng. Ứng dụng thể thao hiển thị ngăn điều hướng, danh sách các môn thể thao và tin tức trên một cửa sổ có kích thước mở rộng.

Hình 1. Ứng dụng thể thao hỗ trợ nhiều kích thước cửa sổ bằng một gói APK hoặc AAB.

Điều kiện tiên quyết

Kiến thức bạn sẽ học được

  • Cách hỗ trợ thay đổi cấu hình.
  • Cách thêm bố cục thay thế ít phải sửa đổi mã hơn.
  • Cách triển khai giao diện người dùng chi tiết theo danh sách, hoạt động theo cách khác nhau cho các kích thước cửa sổ khác nhau.

Sản phẩm bạn sẽ tạo ra

Một ứng dụng Android hỗ trợ:

  • Thiết bị xoay ngang
  • Máy tính bảng, máy tính để bàn và thiết bị di động
  • Danh sách chi tiết hành vi cho các kích thước màn hình khác nhau

Bạn cần có

2. Bắt đầu thiết lập

Tải mã xuống cho lớp học lập trình này và thiết lập dự án:

  1. Từ dòng lệnh, hãy sao chép mã trong kho lưu trữ GitHub:
$ git clone https://github.com/android/add-adaptive-layouts
  1. Trong Android Studio, hãy mở dự án AddingAdaptiveLayout. Dự án được xây dựng trong nhiều nhánh git.
  • Nhánh main chứa mã khởi đầu cho dự án này. Bạn thực hiện các thay đổi đối với nhánh này để hoàn tất lớp học lập trình.
  • Nhánh end chứa giải pháp cho lớp học lập trình này.

3. Di chuyển thành phần điều hướng trên cùng vào Compose

Ứng dụng thể thao sử dụng trình đơn điều hướng dưới cùng làm thành phần điều hướng trên cùng. Thành phần điều hướng này được triển khai bằng lớp BottomNavigationView. Trong phần này, bạn sẽ di chuyển thành phần điều hướng trên cùng sang Compose.

Trình bày nội dung của tài nguyên trình đơn liên kết dưới dạng một lớp kín

  1. Tạo một lớp kín MenuItem để trình bày nội dung của tệp navigation_menu.xml, sau đó truyền cho nó một tham số iconId, một tham số labelId và một tham số destinationId.
  2. Thêm một đối tượng Home, một đối tượng Favorite và một đối tượng Settings dưới dạng lớp con tương ứng với nơi đến.
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
    )
}

Triển khai trình đơn điều hướng dưới cùng ở dạng hàm kết hợp

  1. Xác định một hàm kết hợp BottomNavigationBar nhận 3 tham số sau: một đối tượng menuItems được đặt thành giá trị List<MenuItem>, một đối tượng modifier được đặt thành giá trị Modifier = Modifier và một đối tượng onMenuSelected được đặt thành hàm lambda (MenuItem) -> Unit = {}.
  2. Trong phần nội dung của hàm kết hợp BottomNavigationBar, hãy gọi hàm NavigationBar() để nhận tham số modifier.
  3. Trong hàm lambda được truyền vào hàm NavigationBar(), hãy gọi phương thức forEach() trên tham số menuItems, sau đó gọi một hàm NavigationBarItem() trong hàm lambda được đặt thành lệnh gọi phương thức foreach().
  4. Truyền cho hàm NavigationBarItem() một tham số selected được đặt thành giá trị false, một tham số onClick được đặt thành hàm lambda chứa hàm onMenuSelected có tham số MenuItem, một tham số icon được đặt thành hàm lambda chứa hàm Icon lấy tham số painter = painterResource(id = menuItem.iconId) và tham số contentDescription = nullcùng tham số label được đặt thành hàm lambda chứa hàm Text lấy tham số (text = stringResource(id = menuItem.labelId).
@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))}
            )
        }
    }
}

Thay thế phần tử BottomNavigationView bằng phần tử ComposeView trong tệp tài nguyên bố cục

  • Trong tệp activity_main.xml, hãy thay thế phần tử BottomNavigationView bằng phần tử ComposeView. Thao tác này sẽ nhúng thành phần điều hướng dưới cùng vào một bố cục giao diện người dùng dựa trên chế độ xem bằng tính năng Compose.

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>

Tích hợp trình đơn điều hướng dưới cùng dựa trên Compose với bố cục giao diện người dùng dựa trên chế độ xem

  1. Trong tệp MainActivity.kt, hãy xác định một biến navigationMenuItems được đặt thành danh sách các đối tượng MenuItem. Các đối tượng MenuItems sẽ xuất hiện trong trình đơn điều hướng dưới cùng theo thứ tự danh sách.
  2. Gọi hàm BottomNavigationBar() để nhúng trình đơn điều hướng dưới cùng vào đối tượng ComposeView.
  3. Chuyển đến đích đến liên kết với mục do người dùng chọn trong hàm callback được truyền vào hàm 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. Hỗ trợ hướng ngang

Nếu ứng dụng của bạn hỗ trợ thiết bị màn hình lớn, nó được kỳ vọng sẽ hỗ trợ cả hướng ngang và dọc. Hiện tại, ứng dụng của bạn chỉ có một hoạt động: hoạt động trên MainActivity.

Hướng hiển thị của hoạt động trên thiết bị được đặt trong tệp AndroidManifest.xml có thuộc tính android:screenOrientation, được đặt thành giá trị portrait.

Cách tạo ứng dụng hỗ trợ theo hướng ngang

  1. Đặt thuộc tính android:screenOrientation thành giá trị fullUser. Với cấu hình này, người dùng có thể khoá hướng màn hình. Hướng được xác định bằng cảm biến hướng thiết bị cho một trong bốn hướng.

AndroidManifest.xml

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

Ứng dụng thể thao hỗ trợ hướng ngang bằng cách đặt giá trị fullUser thành thuộc tính android:screenOrientation của một phần tử hoạt động trong tệp AndroidManifest.xml.

Hình 2. Ứng dụng sẽ chạy theo hướng ngang sau khi bạn cập nhật tệp AndroidManifest.xml.

5. Lớp kích thước cửa sổ

Đây là các giá trị điểm ngắt giúp phân loại kích thước cửa sổ thành các lớp kích thước được xác định trước: nhỏ gọn, trung bình và mở rộng, với kích thước cửa sổ thô có sẵn cho ứng dụng. Bạn sẽ dùng các lớp kích thước này khi thiết kế, phát triển và thử nghiệm bố cục thích ứng.

Chiều rộng và chiều cao có sẵn được phân vùng độc lập để ứng dụng luôn liên kết với hai lớp kích thước cửa sổ: lớp kích thước cửa sổ chiều rộng và lớp kích thước cửa sổ chiều cao.

Có hai điểm ngắt giữa ba lớp kích thước cửa sổ theo chiều rộng. Giá trị 600dp là điểm ngắt giữa nhỏ gọn và trung bình, còn 840dp là giá trị giữa các lớp có kích thước cửa sổ mở rộng và trung bình.

Hình 3. Các lớp có kích thước cửa sổ theo chiều rộng và các điểm ngắt được liên kết.

Có hai điểm ngắt cho ba lớp có kích thước cửa sổ theo chiều cao. Giá trị 480dp là điểm ngắt giữa các lớp có kích thước cửa sổ theo chiều cao thấp và trung bình và 900dp là giá trị giữa các lớp có kích thước cửa sổ theo chiều cao trung bình và mở rộng.

Hình 4. Các lớp có kích thước cửa sổ theo chiều cao và điểm ngắt liên kết.

Các lớp kích thước cửa sổ thể hiện kích thước cửa sổ hiện tại của ứng dụng. Nói cách khác, bạn không thể xác định lớp kích thước cửa sổ theo kích thước thiết bị thực tế. Ngay cả khi ứng dụng của bạn chạy trên cùng một thiết bị, lớp kích thước cửa sổ được liên kết vẫn thay đổi theo cấu hình, chẳng hạn như khi bạn chạy ứng dụng ở chế độ chia đôi màn hình. Điều này có hai hệ quả quan trọng:

  • Thiết bị thực tế không đảm bảo một lớp có kích thước cửa sổ cụ thể.
  • Lớp kích thước cửa sổ có thể thay đổi trong suốt thời gian hoạt động của ứng dụng.

Sau khi thêm bố cục thích ứng vào ứng dụng, bạn sẽ thử nghiệm ứng dụng trên tất cả các kích thước cửa sổ, đặc biệt là trên các lớp có kích thước cửa sổ nhỏ, trung bình và mở rộng. Việc kiểm tra cho từng lớp kích thước cửa sổ là cần thiết, nhưng không đủ trong nhiều trường hợp. Điều quan trọng là bạn phải thử nghiệm ứng dụng trên nhiều kích thước cửa sổ để có thể đảm bảo giao diện người dùng điều chỉnh đúng cách. Để biết thêm thông tin, hãy xem phần Bố cục màn hình lớn Chất lượng ứng dụng trên màn hình lớn.

6. Giới thiệu các ngăn danh sách và chi tiết cạnh nhau trên màn hình lớn

Một danh sách chi tiết giao diện người dùng có thể cần hoạt động khác nhau theo lớp kích thước cửa sổ theo chiều rộng hiện tại. Khi lớp kích thước cửa sổ theo chiều rộng trung bình hoặc mở rộng được liên kết với ứng dụng, có nghĩa là ứng dụng có thể có đủ không gian để hiện ngăn danh sách và ngăn chi tiết cạnh nhau, nhờ đó người dùng có thể xem danh sách các mục và thông tin chi tiết mục đã chọn mà không cần phải chuyển đổi màn hình. Tuy nhiên, nếu hiện theo cách này thì các màn hình nhỏ có thể trở nên quá chật chội, do đó tốt hơn nên hiện từng ngăn một với ngăn danh sách được hiện ban đầu. Ngăn chi tiết hiển thị thông tin chi tiết về mục đã chọn khi người dùng nhấn vào một mục trong danh sách. Lớp SlidingPaneLayout quản lý logic để xác định trải nghiệm người dùng nào trong số hai trải nghiệm người dùng này phù hợp với kích thước cửa sổ hiện tại.

Định cấu hình bố cục ngăn danh sách

Lớp SlidingPaneLayout là thành phần giao diện người dùng dựa trên chế độ xem. Bạn sửa đổi tệp tài nguyên bố cục cho ngăn danh sách, đó là tệp fragment_sports_list.xml trong lớp học lập trình này.

Lớp SlidingPaneLayout có 2 phần tử con. Thuộc tính chiều rộng và trọng số của từng phần tử con là các yếu tố chính cho lớp SlidingPaneLayout để xác định xem cửa sổ có đủ lớn để hiển thị cả hai chế độ xem cạnh nhau hay không. Nếu không, danh sách toàn màn hình sẽ được thay thế bằng giao diện người dùng toàn màn hình. Giá trị trọng số được gọi là kích thước hai ngăn theo tỷ lệ khi cửa sổ có kích thước lớn hơn yêu cầu tối thiểu để hiện các ngăn cạnh nhau.

Lớp SlidingPaneLayout đã được áp dụng cho tệp fragment_sports_list.xml. Ngăn danh sách được định cấu hình để có chiều rộng là 1280dp. Đây là lý do khiến ngăn danh sách và thông tin chi tiết không hiển thị cạnh nhau.

Cách làm cho các ngăn hiển thị cạnh nhau khi màn hình lớn hơn 580dp chiều rộng:

  • Đặt RecyclerView thành 280dp chiều rộng.

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>

Sau khi bạn cập nhật tệp sports_list_fragment.xml, ứng dụng thể thao sẽ hiển thị danh sách các môn thể thao và tin tức về thể thao được chọn cạnh nhau.

Hình 5. Danh sách và ngăn chi tiết hiển thị cạnh nhau sau khi bạn cập nhật tệp tài nguyên bố cục.

Hoán đổi ngăn chi tiết

Lúc này, ngăn danh sách và thông tin chi tiết hiện cạnh nhau khi lớp kích thước cửa sổ có chiều rộng trung bình hoặc mở rộng được liên kết với ứng dụng. Tuy nhiên, sẽ xảy ra trường hợp màn hình chuyển đổi hoàn toàn sang ngăn chi tiết khi người dùng chọn một mục từ ngăn danh sách.

Ứng dụng sẽ chuyển màn hình và chỉ hiển thị tin tức thể thao, trong khi ứng dụng dự kiến sẽ tiếp tục hiển thị danh sách các môn thể thao và tin tức cạnh nhau.

Hình 6. Màn hình sẽ chuyển sang ngăn chi tiết sau khi bạn chọn một môn thể thao từ danh sách.

Vấn đề này là do việc di chuyển được kích hoạt khi người dùng chọn một mục trong ngăn danh sách. Mã liên quan có trong tệp SportsListFragment.kt.

SportsListFragment.kt

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

Đảm bảo việc màn hình chỉ chuyển hoàn toàn sang ngăn chi tiết khi không có đủ chỗ để hiển thị danh sách và ngăn chi tiết cạnh nhau:

  • Trong biến hàm adapter, hãy thêm một câu lệnh if để kiểm tra xem thuộc tính isSlidable của lớp SlidingPaneLayout có đúng hay không và thuộc tính isOpen của lớp SlidingPaneLayout có phải là false.

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. Chọn đúng thành phần điều hướng theo lớp kích thước cửa sổ theo chiều rộng

Material Design muốn ứng dụng của bạn chọn các thành phần có tính thích ứng. Trong phần này, bạn chọn thành phần điều hướng cho thanh điều hướng trên cùng dựa trên lớp kích thước cửa sổ theo chiều rộng hiện tại. Bảng này mô tả thành phần điều hướng dự kiến cho mỗi lớp kích thước cửa sổ:

Lớp kích thước cửa sổ theo chiều rộng

Thành phần điều hướng

Nhỏ

Thanh điều hướng dưới cùng

Trung bình

Dải điều hướng

Mở rộng

Ngăn điều hướng cố định

Triển khai dải điều hướng

  1. Tạo một hàm kết hợp NavRail(), nhận 3 tham số: một đối tượng menuItems được đặt thành một giá trị List<MenuItem>, một đối tượng modifier được đặt thành một giá trị Modifier và một hàm lambda onMenuSelected.
  2. Trong phần nội dung hàm, hãy gọi một hàm NavigationRail() lấy đối tượng modifier làm tham số.
  3. Gọi hàm NavigationRailItem() cho mỗi đối tượng MenuItem trong đối tượng menuItems như đối với hàm NavigationBarItem trong hàm BottomNavigationBar().

Triển khai ngăn điều hướng cố định

  1. Tạo một hàm kết hợp NavigationDrawer(), lấy 3 tham số sau: một đối tượng menuItems được đặt thành giá trị List<MenuItem>, một đối tượng modifier được đặt thành giá trị Modifier và một hàm lambda onMenuSelected.
  2. Trong phần nội dung hàm, hãy gọi một hàm Column() nhận đối tượng modifier làm tham số.
  3. Gọi một hàm Row() cho từng đối tượng MenuItem trong đối tượng menuItems.
  4. Trong phần nội dung của hàm Row(), hãy thêm các nhãn icontext giống như bạn đã làm với hàm NavigationBarItem trong hàm BottomNavigationBar().

Chọn đúng thành phần điều hướng theo lớp kích thước chiều rộng cửa sổ

  1. Để truy xuất lớp kích thước cửa sổ theo chiều rộng hiện tại, hãy gọi hàm rememberWidthSizeClass() bên trong hàm kết hợp được truyền đến phương thức setContent() trên đối tượng ComposeView.
  2. Tạo nhánh có điều kiện để chọn thành phần điều hướng dựa trên lớp kích thước cửa sổ theo chiều rộng được truy xuất và gọi thành phần đã chọn.
  3. Truyền một đối tượng Modifier cho hàm NavigationDrawer để chỉ định chiều rộng của đối tượng này làm giá trị 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)
                }
        }

    }
}

Đặt thành phần điều hướng ở vị trí thích hợp

Bây giờ, ứng dụng của bạn có thể chọn đúng thành phần điều hướng dựa trên lớp kích thước cửa sổ theo chiều rộng hiện tại, nhưng bạn không thể đặt các thành phần đã chọn như mong đợi. Điều này là do phần tử ComposeView được đặt trong phần tử FragmentViewContainer.

Cập nhật tài nguyên bố cục thay thế cho lớp MainActivity:

  1. Mở tệp layout-sw600dp/activity_main.xml.
  2. Cập nhật quy tắc ràng buộc để bố trí phần tử ComposeView và phần tử FragmentContainer theo chiều ngang.

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>

Sau khi sửa đổi, ứng dụng sẽ chọn đúng thành phần điều hướng dựa trên lớp kích thước cửa sổ theo chiều rộng tương ứng. Ảnh chụp màn hình đầu tiên hiện màn hình của lớp kích thước cửa sổ theo chiều rộng trung bình. Ảnh chụp màn hình thứ hai hiển thị màn hình của lớp kích thước cửa sổ theo chiều rộng mở rộng.

Ứng dụng thể thao hiển thị đường dải điều hướng, danh sách môn các thể thao và tin tức khi liên kết với lớp kích thước cửa sổ theo chiều rộng trung bình. Ứng dụng thể thao hiển thị ngăn điều hướng, danh sách các môn thể thao và tin tức trên màn hình chính khi ứng dụng liên kết với lớp kích thước cửa sổ theo chiều rộng mở rộng.

Hình 7. Màn hình cho các lớp kích thước cửa sổ theo chiều rộng trung bình và mở rộng.

8. Xin chúc mừng

Xin chúc mừng! Bạn đã hoàn thành lớp học lập trình này và đã biết cách dùng Compose để thêm bố cục thích ứng vào ứng dụng Android dựa trên khung hiển thị. Trong quá trình học, bạn cũng được tìm hiểu về lớp SlidingPaneLayout, lớp kích thước cửa sổ và lựa chọn thành phần điều hướng dựa trên lớp kích thước cửa sổ theo chiều rộng.

Tìm hiểu thêm