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

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

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

Ngăn chi tiết hiển thị 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ẽ hiển thị cùng với ngăn danh sách.
Sau khi một mục được chọn, 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 chi tiết (vì 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 chi tiết danh sách trong Jetpack Compose. Thành phần này bao bọc ListDetailPaneScaffold và thêm tính năng điều hướng tích hợp sẵn cũng như ảnh động xem trước thao tác quay lại.

Cấu trúc 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 tập hợp các mục.
  2. Ngăn chi tiết: Hiển thị 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 ngữ cảnh khi cần.

Khung này thích ứng dựa trên kích thước cửa sổ:

  • Trong cửa sổ lớn, ngăn danh sách và 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 là một phần của thư viện điều hướng thích ứng Material 3.

Thêm ba phần phụ thuộc liên quan sau 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 khối xây dựng cấp thấp như HingeInfoPosture
  • bố cục thích ứng: Bố cục thích ứng 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 sử dụng tính năng 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 dự đoán 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ợ lưu và khôi phục mục danh sách đã chọn. Sử dụng trình bổ trợ kotlin-parcelize để tạo mã cho bạn.
  2. Tạo ThreePaneScaffoldNavigator bằng rememberListDetailPaneScaffoldNavigator.

Trình điều hướng này 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 giàn giáo (tức là MyItem nào đang hiển thị). Vì loại này có thể phân phối, 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 phương thức triển khai ngăn danh sách cho NavigableListDetailPaneScaffold. Sử dụng AnimatedPane để áp dụng ảnh động ngăn mặc định trong quá trình điều hướng. Sau đó, hãy sử dụng ThreePaneScaffoldNavigator để chuyển đến ngăn chi tiết, ListDetailPaneScaffoldRole.Detail và hiển thị mục đã truyền.

  3. Thêm phần 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 đã điều hướng đế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 để bạn có thể truy cập vào bất kỳ dữ liệu nào mà bạn cần hiển thị.

  1. Bạn có thể thay đổi defaultBackBehavior trong NavigableListDetailPaneScaffold (không bắt buộc). 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 điều hướng quay lại khác, bạn có thể ghi đè hành vi này bằng cách chỉ định một tuỳ 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 các 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 sẽ 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, thao tá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ì đây là 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ập nhật, nhưng ngăn danh sách và ngăn chi tiết vẫn hiển thị. Thao tác nhấn nút quay lại có thể thoát khỏi ứng dụng hoặc quy trình điều hướng hiện tại.
  • Một ngăn: Bạn xem Mục 1, sau đó là Mục 2, thao tác nhấn quay lại sẽ đưa bạn quay lại trực tiếp ngăn danh sách email.

Sử dụng tính năng này khi bạn muốn người dùng nhận thấy các chuyển đổi bố cục khác nhau với mỗi hành động quay lại.

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

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

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. Thao tác nhấn quay lại sẽ khôi phục ngăn chi tiết về Mục 1.
  • Một ngăn: Cùng một nội dung được hoàn nguyên.

Sử dụng tính năng 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 như nhau cho 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, thao tá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 sang đích đến trước đó. Trong ứng dụng email của chúng ta, điều này có nghĩa là chỉ báo hình ảnh của ngăn đã chọn sẽ thay đổi.

Hãy sử dụng tính năng này khi việc duy trì chỉ báo trực quan rõ ràng về thao tác đ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

Tuỳ chọn này chỉ xoá đích đến gần đây nhất khỏi ngăn xếp lui. Sử dụng tuỳ 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 triển khai các bước này, mã của bạn sẽ có dạ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)
            }
        }
    },
)