Đặt thuộc tính vùng chứa

Bạn có thể xác định cấu hình vùng chứa Lưới để tạo bố cục linh hoạt, phù hợp với nhiều kích thước màn hình và loại nội dung. Trang này mô tả cách thực hiện những việc sau:

Xác định lưới

Lưới bao gồm các cột và hàng. Thành phần kết hợp Grid có một tham số config chấp nhận một lambda để xác định các cột và hàng trong GridConfigurationScope. Ví dụ sau đây xác định một lưới có 3 hàng và 2 cột, mỗi hàng và cột có kích thước cố định được chỉ định trong Dp:

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
    }
) {
}

Đặt các mục vào lưới

Grid lấy các phần tử giao diện người dùng trong lambda content và đặt chúng vào các ô lưới. Lưới bố trí các mục bất kể bạn có xác định rõ ràng các hàng và cột hay không. Theo mặc định, Grid sẽ cố gắng đặt một phần tử trên giao diện người dùng vào ô lưới có sẵn trong hàng; nếu không thể, phần tử này sẽ đặt phần tử đó vào một ô lưới có sẵn trong hàng tiếp theo. Nếu không có ô trống, Grid sẽ tạo một hàng mới.

Trong ví dụ sau, lưới có 6 ô lưới và đặt một thẻ vào mỗi ô (Hình 1). Mỗi ô lưới có kích thước 160dp x 90dp, tổng kích thước lưới là 320dp x 270dp.

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
    }
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

6 thẻ được đặt trong một lưới có 3 hàng và 2 cột.
Hình 1. 6 thẻ được đặt trong một lưới có 3 hàng và 2 cột.

Để thay đổi hành vi mặc định này thành điền theo cột, hãy đặt thuộc tính flow thành GridFlow.Column.

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
        gap(8.dp)
        flow = GridFlow.Column // Grid tries to place items to fill the column
    },
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

Hàm luồng thay đổi hướng để đặt các mục.
Hình 2. GridFlow.Row (trái) và GridFlow.Column (phải).

Quản lý kích thước kênh

Hàng và cột được gọi chung là làn lưới. Bạn có thể chỉ định kích thước của một đường lưới bằng một trong các phương thức sau:

  • Cố định (Dp): Phân bổ một kích thước cụ thể (ví dụ: column(180.dp)).
  • Tỷ lệ phần trăm (Float): Phân bổ một tỷ lệ phần trăm trong tổng không gian có sẵn từ 0.0f đến 1.0f (ví dụ: row(0.5f) cho 50%).
  • Linh hoạt (Fr): Phân bổ không gian còn lại theo tỷ lệ sau khi tính toán các phần cố định và phần trăm. Ví dụ: nếu hai hàng được đặt thành 1.fr3.fr, thì hàng thứ hai sẽ nhận được 75% chiều cao còn lại.
  • Nội tại: Điều chỉnh kích thước của bản phụ đề dựa trên nội dung bên trong. Để biết thêm thông tin, hãy xem bài viết Xác định kích thước của đường lưới một cách tự nhiên.

Ví dụ sau đây sử dụng các lựa chọn định cỡ đường chạy khác nhau để xác định chiều cao hàng:

Grid(
    config = {
        column(1f)

        row(100.dp)
        row(0.2f)
        row(1.fr)
        row(GridTrackSize.Auto)
    },
    modifier = Modifier.height(480.dp)
) {
    PastelRedCard("Fixed(100.dp)")
        PastelGreenCard("Percentage(0.2f)")
    PastelBlueCard("Flex(1.fr)")
        PastelYellowCard("Auto")

}

Chiều cao hàng được xác định bằng 4 lựa chọn chính về kích thước của các thành phần.
Hình 3. Chiều cao hàng được xác định bằng 4 lựa chọn chính về kích thước của các rãnh trong Grid.

Đặt kích thước tối thiểu cho các đường lưới linh hoạt

Khi vùng chứa lưới không còn khoảng trống, một thành phần linh hoạt tiêu chuẩn có thể thu nhỏ thành 0.dp. Để ngăn tình trạng này và đảm bảo nội dung không bị nén, hãy sử dụng GridTrackSize.MinMax để áp dụng kích thước tối thiểu rõ ràng trong khi vẫn giữ cho bản nhạc linh hoạt.

Ví dụ sau đây phân bổ ít nhất 100.dp cho hàng đầu tiên:

Grid(
    config = {
        column(1f)
        // The first row has a minimum height of 100.dp and can expand to 
        // the half of the remaining space.
        row(GridTrackSize.MinMax(100.dp, 1.fr))
        // The second row takes the half of the remaining space.
        row(1.fr)
        // The third row has a fixed height of 200.dp.
        row(200.dp)
    },
    modifier = Modifier.size(360.dp) // Total grid height is 360.dp
) {
    PastelRedCard("MinMax(100.dp, 1.fr)")
        PastelGreenCard("Flex(1.fr)")
    PastelBlueCard("Fixed(200.dp)")
}

Chiều cao hàng được xác định bằng 4 lựa chọn chính về kích thước của các thành phần.
Hình 4. Hàng đầu tiên có chiều cao ít nhất là 100.dp.

Đặt kích thước tối thiểu của phần tử lưới để đặt danh sách tải từng phần

Các thành phần theo dõi linh hoạt tiêu chuẩn sẽ tự động truy vấn kích thước nội tại của các thành phần con để thiết lập kích thước cơ sở. Tuy nhiên, Jetpack Compose cấm truy vấn kích thước nội tại của SubcomposeLayout, hỗ trợ các thành phần, chẳng hạn như LazyColumnLazyRow.

Việc đặt một danh sách lazy bên trong một thành phần theo dõi linh hoạt tiêu chuẩn sẽ gây ra sự cố IllegalStateException. Để đặt danh sách tải từng phần một cách an toàn vào bên trong một ô lưới linh hoạt, hãy dùng MinMax với kích thước tối thiểu rõ ràng (chẳng hạn như 0.dp) để bỏ qua quá trình đo lường nội tại.

Grid(
    config = {
        column(1f)
        // The first row's height is determined by the height of the Text composable.
        row(GridTrackSize.Auto)
        // The second row occupies the remaining space, allowing the LazyColumn to scroll.
        row(GridTrackSize.MinMax(0.dp, 1.fr))

        gap(8.dp)
    },
    modifier = Modifier.size(width = 170.dp, height = 240.dp)
) {
    Text("LazyColumn in a Grid")
    // The LazyColumn is placed in the second row, filling the remaining space.
    LazyColumn(verticalArrangement = Arrangement.spacedBy(4.dp)) {
        items(100) { number ->
            PastelGreenCard("Card $number")
        }
    }
}

Chiều cao hàng được xác định bằng 4 lựa chọn chính về kích thước của các thành phần.
Hình 5. LazyColumn trong một ô lưới.

Xác định kích thước đường lưới một cách tự nhiên

Bạn có thể sử dụng kích thước nội tại cho Grid khi muốn bố cục thích ứng với nội dung thay vì ép buộc nội dung vào một vùng chứa cố định. Kích thước của đường lưới được xác định bằng các giá trị sau:

  • GridTrackSize.MaxContent: Sử dụng kích thước nội dung tối đa vốn có (ví dụ: chiều rộng được xác định bằng độ dài đầy đủ của văn bản trong một khối văn bản không có tính năng xuống dòng).
  • GridTrackSize.MinContent: Sử dụng kích thước tối thiểu vốn có của nội dung (ví dụ: chiều rộng được xác định bằng từ dài nhất trong một khối văn bản).
  • GridTrackSize.Auto: Sử dụng kích thước linh hoạt cho một bản nhạc thích ứng dựa trên không gian có sẵn. Theo mặc định, thành phần này hoạt động như MaxContent, nhưng sẽ thu nhỏ và bao bọc nội dung để vừa với vùng chứa mẹ.

Ví dụ sau đây đặt hai văn bản cạnh nhau. Kích thước cột cho văn bản đầu tiên được xác định bằng chiều rộng tối thiểu cần thiết để hiển thị văn bản, còn chiều rộng cột thứ hai phụ thuộc vào chiều rộng tối đa cần thiết của văn bản.

Grid(
    config = {
        column(GridTrackSize.MinContent)
        column(GridTrackSize.MaxContent)
        row(1.0f)
    },
    modifier = Modifier.width(480.dp)
) {
    Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras imperdiet.")
    Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras imperdiet.")
}

Kích thước nội tại được chỉ định trong các cột.
Hình 5. Kích thước nội tại được chỉ định trong các cột.

Đặt khoảng cách giữa các hàng và cột

Sau khi điều chỉnh kích thước các rãnh lưới, bạn có thể sửa đổi khoảng trống giữa các rãnh để tinh chỉnh khoảng cách giữa các rãnh. Bạn có thể chỉ định khoảng cách giữa các cột bằng hàm columnGap và khoảng cách giữa các hàng bằng rowGap. Trong ví dụ sau, có một khoảng trống 16dp giữa mỗi hàng và một khoảng trống 8dp giữa mỗi cột (Hình 5).

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
        rowGap(16.dp)
        columnGap(8.dp)
    }
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

Khoảng trống giữa các hàng và cột.
Hình 6. Khoảng trống giữa các hàng và cột.

Bạn cũng có thể dùng hàm tiện ích gap để xác định khoảng trống có cùng kích thước cột và hàng, đồng thời xác định riêng kích thước cột và khoảng trống bằng một hàm duy nhất. Mã sau đây sẽ thêm khoảng trống 8dp vào lưới:

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
        gap(8.dp) // Equivalent to columnGap(8.dp) and rowGap(8.dp)
    }
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

Xác định các khu vực trên lưới bằng các khu vực được đặt tên

Các khu vực được đặt tên cho phép bạn đính kèm tên vào các nhóm ô lưới, được gọi là khu vực lưới. Bạn có thể sử dụng các tên này thay vì chỉ mục toạ độ khi đặt các phần tử trên giao diện người dùng vào lưới.

Việc sử dụng các vùng được đặt tên mang lại 2 lợi ích chính cho khả năng đọc mã:

  • Khi xác định bố cục dạng lưới, mục đích và vị trí của nội dung dự kiến sẽ rõ ràng.
  • Khi thêm nội dung, mục đích của nội dung đó phải rõ ràng.

Để sắp xếp bố cục phức tạp một cách rõ ràng, bạn có thể tách cấu trúc lưới thực tế khỏi vị trí của thành phần con bằng cách xác định các vùng lưới ngữ nghĩa.

Bên trong hàm lambda config, hãy dùng hàm area trong GridConfigurationScope để đăng ký các vùng được đặt tên trong lưới. Sau đó, bạn có thể chỉ định các thành phần kết hợp con cho những vùng này bằng cách sử dụng đối tượng sửa đổi gridItem với giá trị nhận dạng vùng tương ứng. Hàm area ánh xạ một giá trị nhận dạng ngữ nghĩa (chẳng hạn như giá trị lớp enum hoặc khoá chuỗi) đến một tập hợp toạ độ lưới thực. Đường lưới và chỉ mục là dựa trên 1 (tức là hàng đầu tiên là 1 và cột đầu tiên là 1).

Ví dụ: bạn xác định một lưới có 4 mã nhận dạng khu vực:

/**
 * An enum representing the IDs for named areas within the grid.
 */
enum class GridAreaNames {
    Area1,
    Area2,
    Area3,
    Area4
}

Cung cấp tên cho khu vực bằng cách sử dụng tham số areaId cùng với toạ độ và khoảng của ô trong khu vực. Công cụ sửa đổi gridItem sử dụng areaId làm khoá để chỉ định từng mục con cho vùng lưới được chỉ định, như minh hoạ trong ví dụ sau:

Grid(
    config = {
        // Define a single column that takes all available width.
        repeat(2) { column(0.5f) }

        // Define four rows, each taking 25% of the total height.
        repeat(4) { row(0.25f) }

        // Define named grid areas by associating an areaId with specific row and column indices.
        // Row and column indices are 1-based.
        area(areaId = GridAreaNames.Area1, row = 1, column = 1, columnSpan = 2)
        area(areaId = GridAreaNames.Area2, row = 2, column = 1, rowSpan = 3)
        area(areaId = GridAreaNames.Area3, rows = 2..3, columns = 2..2)
        area(areaId = GridAreaNames.Area4, row = 4, column = 2)

        gap(4.dp)
    },
    modifier = Modifier.size(360.dp)
) {
    PastelRedCard(
        "Area 1",
        // Use Modifier.gridItem(areaId) to place this composable into the
        // grid area defined with the matching ID in the config block.
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area1)
    )
    PastelGreenCard(
        "Area 2",
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area2)
    )
    PastelBlueCard(
        "Area 3",
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area3)
    )
    PastelYellowCard(
        "Area 4",
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area4)
    )
}

Bằng cách sử dụng các vùng được đặt tên, bạn có thể sắp xếp lại hoặc điều chỉnh lưới bố cục thực (ví dụ: thay đổi hàng, cột hoặc kích thước đường kẻ) trong lambda config mà không cần sửa đổi thứ tự hoặc tham số của các thành phần kết hợp con.