Xây dựng bố cục danh sách-chi tiết

List-detail là một mẫu giao diện người dùng bao gồm bố cục hai ngăn, trong đó một ngăn trình bày danh sách các mục và một ngăn khác hiển thị thông tin chi tiết của các mục được chọn trong danh sách.

Mẫu này đặc biệt hữu ích cho những ứng dụng cung cấp thông tin chi tiết về các phần tử của bộ sưu tập lớn, chẳng hạn như một ứng dụng email có danh sách email và nội dung chi tiết của từng thư email. Bạn cũng có thể dùng danh sách-chi tiết cho các đường dẫn ít quan trọng hơn, chẳng hạn như chia các lựa chọn ưu tiên của ứng dụng thành một danh sách các danh mục với các lựa chọn ưu tiên cho từng danh mục trong ngăn chi tiết.

Ngăn chi tiết xuất hiện cùng với trang danh sách.
Hình 1. Khi có đủ kích thước màn hình, ngăn chi tiết sẽ xuất hiện cùng với ngăn danh sách.
Sau khi bạn chọn một mục, ngăn chi tiết sẽ chiếm toàn bộ màn hình.
Hình 2. Khi kích thước màn hình bị hạn chế, ngăn thông tin chi tiết (kể từ khi một mục được chọn) sẽ chiếm toàn bộ không gian.

Triển khai Mẫu danh sách-chi tiết bằng NavigableListDetailPaneScaffold

NavigableListDetailPaneScaffold là một thành phần kết hợp giúp đơn giản hoá việc triển khai bố cục danh sách-chi tiết trong Jetpack Compose. Thành phần này bao bọc ListDetailPaneScaffold và thêm ảnh động xem trước thao tác quay lại cũng như ảnh động điều hướng tích hợp sẵn.

Khung hiển thị danh sách-chi tiết hỗ trợ tối đa 3 ngăn:

  1. Ngăn danh sách: Hiển thị một bộ sưu tập các mục.
  2. Ngăn chi tiết: Cho biết thông tin chi tiết về một mục đã chọn.
  3. Ngăn bổ sung (không bắt buộc): Cung cấp thêm bối cảnh khi cần.

Khung hiển thị sẽ điều chỉnh dựa trên kích thước cửa sổ:

  • Trong các cửa sổ lớn, ngăn danh sách và ngăn chi tiết sẽ xuất hiện cạnh nhau.
  • Trong các cửa sổ nhỏ, mỗi lần chỉ có một ngăn hiển thị, chuyển đổi khi người dùng di chuyển.

Khai báo phần phụ thuộc

NavigableListDetailPaneScaffold thuộc thư viện điều hướng thích ứng Material 3.

Thêm 3 phần phụ thuộc có liên quan sau đây vào tệp build.gradle của ứng dụng hoặc mô-đun:

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'
  • thích ứng: Các thành phần cấp thấp như HingeInfoPosture
  • adaptive-layout: Bố cục thích ứng, chẳng hạn như ListDetailPaneScaffoldSupportingPaneScaffold
  • adaptive-navigation: Các thành phần kết hợp để điều hướng trong và giữa các ngăn, cũng như các bố cục thích ứng hỗ trợ điều hướng theo mặc định, chẳng hạn như NavigableListDetailPaneScaffoldNavigableSupportingPaneScaffold

Đảm bảo dự án của bạn có compose-material3-adaptive phiên bản 1.1.0-beta1 trở lên.

Chọn sử dụng tính năng xem trước thao tác quay lại

Để bật ảnh động xem trước thao tác quay lại trong Android 15 trở xuống, bạn phải chọn hỗ trợ cử chỉ xem trước thao tác quay lại. Để chọn sử dụng, hãy thêm android:enableOnBackInvokedCallback="true" vào thẻ <application> hoặc các thẻ <activity> riêng lẻ trong tệp AndroidManifest.xml. Để biết thêm thông tin, hãy xem phần Chọn sử dụng tính năng xem trước thao tác quay lại.

Khi ứng dụng của bạn nhắm đến Android 16 (API cấp 36) trở lên, tính năng xem trước thao tác quay lại sẽ được bật theo mặc định.

Cách sử dụng cơ bản

Triển khai NavigableListDetailPaneScaffold như sau:

  1. Sử dụng một lớp đại diện cho nội dung đã chọn. Sử dụng lớp Parcelable để hỗ trợ việc lưu và khôi phục mục đã chọn trong danh sách. Dùng trình bổ trợ kotlin-parcelize để tạo mã cho bạn.
  2. Tạo một ThreePaneScaffoldNavigator bằng rememberListDetailPaneScaffoldNavigator.

Trình điều hướng này được dùng để di chuyển giữa danh sách, chi tiết và các ngăn bổ sung. Bằng cách khai báo một loại chung, trình điều hướng cũng theo dõi trạng thái của khung hiển thị (tức là MyItem nào đang được hiển thị). Vì loại này có thể phân chia, nên trình điều hướng có thể lưu và khôi phục trạng thái để tự động xử lý các thay đổi về cấu hình.

  1. Truyền trình điều hướng đến thành phần kết hợp NavigableListDetailPaneScaffold.

  2. Cung cấp chế độ triển khai ngăn danh sách cho NavigableListDetailPaneScaffold. Sử dụng AnimatedPane để áp dụng ảnh động mặc định cho ngăn trong quá trình điều hướng. Sau đó, hãy dùng ThreePaneScaffoldNavigator để chuyển đến ngăn chi tiết, ListDetailPaneScaffoldRole.Detail và hiển thị mặt hàng đã truyền.

  3. Thêm quá trình triển khai ngăn chi tiết vào NavigableListDetailPaneScaffold.

Khi quá trình điều hướng hoàn tất, currentDestination sẽ chứa ngăn mà ứng dụng của bạn đã chuyển đến, bao gồm cả nội dung hiển thị trong ngăn đó. Thuộc tính contentKey có cùng loại được chỉ định trong lệnh gọi ban đầu, vì vậy, bạn có thể truy cập vào mọi dữ liệu cần hiển thị.

  1. Bạn có thể thay đổi defaultBackBehavior trong NavigableListDetailPaneScaffold. Theo mặc định, NavigableListDetailPaneScaffold sử dụng PopUntilScaffoldValueChange cho defaultBackBehavior.

Nếu ứng dụng của bạn yêu cầu một mẫu thao tác quay lại khác, bạn có thể ghi đè hành vi này bằng cách chỉ định một lựa chọn BackNavigationBehavior khác.

BackNavigationBehavior lựa chọn

Phần sau đây sử dụng ví dụ về một ứng dụng email có danh sách email trong một ngăn và chế độ xem chi tiết trong ngăn còn lại.

Hành vi này tập trung vào những thay đổi đối với cấu trúc bố cục tổng thể. Trong chế độ thiết lập nhiều ngăn, việc thay đổi nội dung email trong ngăn chi tiết không làm thay đổi cấu trúc bố cục cơ bản. Do đó, nút quay lại có thể thoát khỏi ứng dụng hoặc biểu đồ điều hướng hiện tại vì không có thay đổi bố cục nào để quay lại trong ngữ cảnh hiện tại. Trong bố cục một ngăn, việc nhấn nút quay lại sẽ bỏ qua các thay đổi về nội dung trong chế độ xem chi tiết và quay lại chế độ xem danh sách, vì thao tác này thể hiện một thay đổi rõ ràng về bố cục.

Hãy xem các ví dụ sau đây:

  • Nhiều ngăn: Bạn đang xem một email (Mục 1) trong ngăn chi tiết. Khi nhấp vào một email khác (Mục 2), ngăn chi tiết sẽ được cập nhật, nhưng ngăn danh sách và ngăn chi tiết vẫn hiển thị. Khi nhấn nút quay lại, ứng dụng hoặc luồng điều hướng hiện tại có thể thoát.
  • Một ngăn: Bạn xem Mục 1, sau đó xem Mục 2, khi nhấn nút quay lại, bạn sẽ được chuyển thẳng đến ngăn danh sách email.

Hãy sử dụng lựa chọn này khi bạn muốn người dùng nhận thấy các hiệu ứng chuyển đổi bố cục riêng biệt với mỗi thao tác quay lại.

Thay đổi giá trị điều hướng.
PopUntilContentChange

Hành vi này ưu tiên nội dung được hiển thị. Nếu bạn xem Mục 1 rồi đến Mục 2, thì khi nhấn nút quay lại, bạn sẽ quay về Mục 1, bất kể bố cục là gì.

Hãy xem các ví dụ sau đây:

  • Nhiều ngăn: Bạn xem Mục 1 trong ngăn chi tiết, sau đó nhấp vào Mục 2 trong danh sách. Ngăn chi tiết sẽ cập nhật. Khi nhấn nút quay lại, ngăn chi tiết sẽ khôi phục về Mục 1.
  • Một ngăn: Nội dung sẽ quay về phiên bản cũ.

Hãy dùng nút này khi người dùng muốn quay lại nội dung đã xem trước đó bằng thao tác quay lại.

quá trình chuyển đổi giữa hai ngăn chi tiết
PopUntilCurrentDestinationChange

Hành vi này sẽ đẩy ngăn xếp lui cho đến khi đích đến điều hướng hiện tại thay đổi. Điều này áp dụng cho cả bố cục một ngăn và nhiều ngăn.

Hãy xem các ví dụ sau đây:

Bất kể bạn đang ở bố cục một ngăn hay nhiều ngăn, việc nhấn nút quay lại sẽ luôn di chuyển tiêu điểm từ phần tử điều hướng được làm nổi bật đến đích đến trước đó. Trong ứng dụng email của chúng tôi, điều này có nghĩa là chỉ báo trực quan của ngăn đã chọn sẽ thay đổi.

Hãy sử dụng lựa chọn này khi việc duy trì chỉ báo trực quan rõ ràng về chế độ điều hướng hiện tại là yếu tố quan trọng đối với trải nghiệm người dùng.

di chuyển giữa ngăn chi tiết và ngăn danh sách
PopLatest

Lựa chọn này chỉ xoá đích đến gần đây nhất khỏi ngăn xếp lui. Sử dụng lựa chọn này để điều hướng quay lại mà không bỏ qua các trạng thái trung gian.

Sau khi bạn triển khai các bước này, mã của bạn sẽ trông giống như sau:

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
val scope = rememberCoroutineScope()

NavigableListDetailPaneScaffold(
    navigator = scaffoldNavigator,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    scope.launch {
                        scaffoldNavigator.navigateTo(
                            ListDetailPaneScaffoldRole.Detail,
                            item
                        )
                    }
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            scaffoldNavigator.currentDestination?.contentKey?.let {
                MyDetails(it)
            }
        }
    },
)