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 một bố cục ngăn kép, 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 về các mục được chọn trong 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ụ: ứ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 chi tiết danh sách 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 về ứng dụng thành một danh sách các danh mục có lựa chọn ưu tiên cho từng danh mục trong ngăn chi tiết.

Triển khai mẫu giao diện người dùng bằng ListDetailPaneScaffold

ListDetailPaneScaffold là một thành phần kết hợp giúp đơn giản hoá việc triển khai mẫu danh sách-chi tiết trong ứng dụng của bạn. Bộ khung chi tiết danh sách có thể bao gồm tối đa 3 ngăn: ngăn danh sách, ngăn chi tiết và ngăn bổ sung không bắt buộc. Scaffold xử lý các phép tính không gian màn hình. 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. Trên kích thước màn hình nhỏ, bộ khung sẽ tự động chuyển sang hiển thị danh sách hoặc ngăn chi tiết trên toàn màn hình.

Ngăn chi tiết hiển thị dọc theo 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ị giới hạn, ngăn chi tiết (kể từ một mục đã được chọn) sẽ chiếm toàn bộ không gian.

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

ListDetailPaneScaffold là một phần của thư viện bố cục thích ứng Material 3.

Ứng dụng của bạn phải chứa các phần phụ thuộc cho 3 thư viện Material 3 có liên quan:

  • 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 để di chuyển trong và giữa các ngăn

Thêm các phần phụ thuộc vào tệp build.gradle của ứng dụng hoặc mô-đun:

Kotlin


implementation("androidx.compose.material3.adaptive:adaptive:1.0.0-alpha12")
implementation("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha12")
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha12")

Groovy


implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-alpha12'
implementation 'androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha12'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha12'

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

Triển khai ListDetailPaneScaffold như sau:

  1. Sử dụng một lớp đại diện cho nội dung cần chọn. Lớp này phải là Parcelable để hỗ trợ việc lưu và khôi phục mục trong danh sách đã chọn. Sử dụng trình bổ trợ kotlin-kotlinize để tạo mã cho bạn.

    @Parcelize
    class MyItem(val id: Int) : Parcelable

  2. Tạo ThreePaneScaffoldNavigator bằng rememberListDetailPaneScaffoldNavigator và thêm BackHandler. Trình điều hướng này dùng để di chuyển giữa danh sách, thông tin chi tiết và các ngăn bổ sung. Bằng cách khai báo loại chung, trình điều hướng cũng theo dõi trạng thái của scaffold (nghĩa là MyItem đang được hiển thị). Vì loại này có thể đóng gói, 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. BackHandler hỗ trợ việc điều hướng quay lại bằng cử chỉ quay lại hoặc nút của hệ thống. Hành vi dự kiến của nút quay lại cho ListDetailPaneScaffold phụ thuộc vào kích thước cửa sổ và giá trị scaffold hiện tại. Nếu ListDetailPaneScaffold có thể hỗ trợ quay lại trạng thái hiện tại, thì canNavigateBack() sẽ là true, hãy bật BackHandler.

    val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. Truyền scaffoldState từ navigator sang thành phần kết hợp ListDetailPaneScaffold.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

  4. Cung cấp phương thức triển khai ngăn danh sách cho ListDetailPaneScaffold. Sử dụng AnimatedPane để áp dụng ảnh động của ngăn mặc định trong quá trình điều hướng. Sau đó, sử dụng ThreePaneScaffoldNavigator để chuyển đến ngăn chi tiết, ListDetailPaneScaffoldRole.Detail rồi hiển thị mục đã truyền.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                MyList(
                    onItemClick = { item ->
                        // Navigate to the detail pane with the passed item
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                    }
                )
            }
        },
        // ...
    )

  5. Đưa phương thức triển khai ngăn chi tiết vào ListDetailPaneScaffold. Khi quá trình điều hướng hoàn tất, currentDestination sẽ chứa ngăn mà ứng dụng đã điều hướng đến, bao gồm cả nội dung hiển thị trong ngăn đó. Thuộc tính content là cùng một loại được chỉ định trong lệnh gọi ghi nhớ ban đầu (trong ví dụ này là MyItem), vì vậy, bạn cũng có thể truy cập vào thuộc tính này cho bất kỳ dữ liệu nào bạn cần hiển thị.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane {
                navigator.currentDestination?.content?.let {
                    MyDetails(it)
                }
            }
        },
    )

Sau khi bạn triển khai các bước trên, mã của bạn sẽ có dạng như sau:

val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            navigator.currentDestination?.content?.let {
                MyDetails(it)
            }
        }
    },
)