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

Chi tiết danh sách là mẫu giao diện người dùng bao gồm một bố cục hai ngăn trong đó một ngăn trình bày một 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 đã chọn khỏi danh sách.

Mẫu này đặc biệt hữu ích với các ứng dụng cung cấp thông tin chuyên sâu thông tin về các thành phần của các tập hợ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. Danh sách-chi tiết cũng có thể được dùng cho các đường dẫn ít quan trọng hơn, chẳng hạn như phân chia ứng dụng tùy chọn vào một danh sách danh mục cùng với các tùy chọn cho mỗi 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. Một scaffold (giàn giáo) có thể bao gồm tối đa ba ngăn: ngăn danh sách, ngăn chi tiết và ngăn bổ sung không bắt buộc. Chiến lược phát hành đĩa đơn scaffold xử lý các phép tính không gian màn hình. Khi kích thước màn hình đủ sẵn có, ngăn chi tiết sẽ hiển thị cùng với ngăn danh sách. Trên màn hình nhỏ thì scaffold sẽ tự động chuyển sang hiển thị danh sách hoặc ngăn chi tiết chạy 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, thông tin chi tiết hiển thị bên cạnh 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ừ khi một mục đã được chọn) chiếm lĩnh toàn bộ không gian này.

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

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

Thêm ba 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) – Các 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á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 để được hỗ trợ lưu và khôi phục mục danh sách đã chọn. Sử dụng kotlin-kotlinize plugin để tạo mã cho bạn.

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

  2. Tạo một ThreePaneScaffoldNavigator bằng rememberListDetailPaneScaffoldNavigator rồi thêm một BackHandler. Chiến dịch này trình điều hướng dùng để di chuyển giữa danh sách, thông tin chi tiết và ngăn bổ sung. Theo khai báo loại chung, trình điều hướng cũng theo dõi trạng thái của Scaffold (tức là MyItem đang được hiển thị). Vì loại này là theo gói, trạng thái có thể được trình điều hướng lưu và khôi phục thành tự động xử lý các thay đổi về cấu hình. Chiến lược phát hành đĩa đơn BackHandler cung cấp hỗ trợ để điều hướng trở lại bằng cử chỉ quay lại của hệ thống hoặc . 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à scaffold hiện tại giá trị. Nếu ListDetailPaneScaffold có thể hỗ trợ việc quay lại bằng trạng thái hiện tại, sau đó canNavigateBack()true, sẽ bật BackHandler.

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

  3. Truyền scaffoldState từ navigator đến 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 khi điều hướng. Sau đó sử dụng ThreePaneScaffoldNavigator để điều hướng đến ngăn chi tiết. ListDetailPaneScaffoldRole.Detail rồi cho thấy 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 ứng dụng đã truy cập, bao gồm cả nội dung được hiển thị trong ngăn. Chiến lược phát hành đĩa đơn Thuộc tính content cùng loại được chỉ định trong lệnh gọi ghi nhớ ban đầu (MyItem trong ví dụ này), để bạn cũng có thể truy cập vào thuộc tính cho bất kỳ dữ liệu nào mà 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)
            }
        }
    },
)