Đặ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ó tham số config chấp nhận 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 trong lưới

Grid lấy các phần tử trên 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 đã xác định rõ ràng các hàng và cột hay chưa. 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ử đó sẽ được đặ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ạo nên 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ẻ nội dung được xếp thành một lưới gồm 3 hàng và 2 cột.
Hình 1. 6 thẻ nội dung được xếp thành một lưới gồm 3 hàng và 2 cột.

Để thay đổi hành vi mặc định này thành lấp đầy 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 flow thay đổi hướng đặt các mục.
Hình 2. GridFlow.Row (bên trái) và GridFlow.Column (bên phải).

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

Hàng và cột được gọi chung là đường lưới. Bạn có thể chỉ định kích thước của một dải 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ổ tỷ lệ phần trăm của 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 dải cố định và dải phần trăm. Ví dụ: nếu hai hàng được đặt thành 1.fr và 3.fr, thì hàng sau sẽ nhận được 75% chiều cao còn lại.
  • Intrinsic (Nội tại): Đặt kích thước cho dải dựa trên nội dung bên trong dải. Để biết thêm thông tin, hãy xem bài viết Xác định kích thước dải lưới theo bản chất.

Ví dụ sau đây sử dụng các tuỳ chọn định cỡ dải ô khác nhau để xác định chiều cao của 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 bốn tuỳ chọn định cỡ kênh chính.
Hình 3. Chiều cao hàng được xác định bằng 4 tuỳ chọn định cỡ dải chính trong Grid.

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

Khi vùng chứa lưới không còn không gian, một dải linh hoạt tiêu chuẩn có thể thu nhỏ thành 0.dp. Để ngăn chặn điều này và đảm bảo nội dung không bị dồn nén, hãy sử dụng GridTrackSize.MinMax để thực thi 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 bốn tuỳ chọn định cỡ kênh chính.
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 dải lưới để đặt danh sách tải từng phần

Các dải linh hoạt tiêu chuẩn 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 không cho phép truy vấn kích thước nội tại của SubcomposeLayout, vốn hỗ trợ các thành phần như LazyColumn và LazyRow.

Việc đặt danh sách tải từng phần bên trong một bản nhạc 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 bên trong một dải lưới linh hoạt, hãy sử 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 lượt đ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 bốn tuỳ chọn định cỡ kênh chính.
Hình 5. LazyColumn trong một ô lưới.

Xác định kích thước dải lưới theo bản chất

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ì buộc bố cục phải vừa với một vùng chứa cố định. Kích thước của dải lưới được xác định bằng các giá trị sau:

  • GridTrackSize.MaxContent: Sử dụng kích thước nội tại tối đa của nội dung (ví dụ: chiều rộng được xác định bằng toàn bộ chiều dài của văn bản trong một khối văn bản không xuống dòng).
  • GridTrackSize.MinContent: Sử dụng kích thước nội tại tối thiểu của nội dung (ví dụ: chiều rộng được xác định bằng từ đơn dài nhất trong khối văn bản).
  • GridTrackSize.Auto: Sử dụng kích thước linh hoạt cho một dải 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 giống như MaxContent, nhưng thu nhỏ và gói 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 theo chiều rộng tối thiểu cần thiết để hiển thị văn bản, và 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 định kích thước cho các dải lưới, bạn có thể sửa đổi khoảng trống lưới để tinh chỉnh khoảng cách giữa các dải. 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ó khoảng cách 16dp giữa mỗi hàng và khoảng cách 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 cách giữa các hàng và cột.
Hình 6. Khoảng cách giữa các hàng và cột.

Bạn cũng có thể sử dụng hàm tiện lợi 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 kích thước cột và khoảng trống riêng biệt bằng một hàm duy nhất. Đoạn mã sau đây 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 vùng lưới bằng các vùng được đặt tên

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à vùng 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 có hai lợi ích chính đối với khả năng đọc mã:

  • Khi xác định bố cục 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 các 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 vật lý khỏi vị trí của phần tử 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 sử 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 khu vực này bằng cách sử dụng đối tượng sửa đổi gridItem với giá trị nhận dạng khu vực tương ứng. Hàm area liên kết 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) với một tập hợp toạ độ lưới vật lý. Đường lưới và chỉ mục bắt đầu từ 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ã 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 tham số areaId cùng với toạ độ và khoảng ô của khu vực. Đối tượng 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ư 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 tế (ví dụ: thay đổi hàng, cột hoặc kích thước bản nhạc) 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.