Compose có một quy tắc là bạn chỉ nên đo lường thành phần con một lần; việc đo lường thành phần con hai lần sẽ cho ra một ngoại lệ trong thời gian chạy. Tuy nhiên, có một số trường hợp bạn cần thêm thông tin về thành phần con trước khi đo lường.
Hàm nội tại (intrinsics) cho phép bạn truy vấn thành phần con trước khi thực sự đo lường.
Đối với thành phần kết hợp, bạn có thể yêu cầu intrinsicWidth
hoặc intrinsicHeight
:
(min|max)IntrinsicWidth
: Với chiều rộng này, bạn có thể vẽ nội dung với chiều rộng tối thiểu/tối đa bao nhiêu là phù hợp?(min|max)IntrinsicHeight
: Với chiều cao này, bạn có thể vẽ nội dung với chiều cao tối thiểu/tối đa bao nhiêu là phù hợp?
Ví dụ: nếu bạn yêu cầu minIntrinsicHeight
của một Text
có height
vô hạn, thì thao tác này sẽ trả về height
của Text
như thể văn bản đó được vẽ trong một dòng duy nhất.
Hàm nội tại trong thực tế
Hãy tưởng tượng chúng ta muốn tạo một thành phần kết hợp có thể hiển thị hai đoạn văn bản trên màn hình được phân tách bằng một đường phân cách như sau:
Chúng ta có thể làm việc này bằng cách nào? Chúng ta có thể có một Row
với hai Text
bên trong mở rộng nhiều nhất có thể và một Divider
ở giữa. Chúng ta muốn Divider
cao bằng Text
cao nhất và hẹp (width = 1.dp
).
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) HorizontalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } }
Nếu xem trước, chúng ta sẽ thấy Divider
mở rộng ra toàn màn hình và đó không phải là điều chúng ta muốn:
Điều này xảy ra vì Row
đo lường từng thành phần con riêng lẻ và không thể sử dụng chiều cao của Text
để ràng buộc Divider
. Chúng ta muốn Divider
lấp đầy không gian hiện có với một chiều cao nhất định. Do đó, chúng ta có thể sử dụng phương thức sửa đổi height(IntrinsicSize.Min)
.
height(IntrinsicSize.Min)
cho phép thành phần con cao hơn chiều cao nội tại tối thiểu. Vì có tính đệ quy, lượt truy vấn này sẽ truy vấn Row
và cũng như minIntrinsicHeight
của thành phần con.
Khi áp dụng mã đó, mã sẽ hoạt động như mong đợi:
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier.height(IntrinsicSize.Min)) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) HorizontalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } } // @Preview @Composable fun TwoTextsPreview() { MaterialTheme { Surface { TwoTexts(text1 = "Hi", text2 = "there") } } }
Bản xem trước:
minIntrinsicHeight
của thành phần kết hợp Row
sẽ là minIntrinsicHeight
tối đa của thành phần con. minIntrinsicHeight
của phần tử Divider
là 0 vì không chiếm dung lượng nếu không có điều kiện ràng buộc nào được đưa ra; Text
minIntrinsicHeight
sẽ bằng của văn bản đã cung cấp một width
cụ thể. Do đó, điều kiện ràng buộc height
của phần tử Row
sẽ là minIntrinsicHeight
tối đa của Text
. Sau đó, Divider
sẽ mở rộng height
tới giới hạn height
do Row
đưa ra.
Hàm nội tại trong bố cục tuỳ chỉnh
Khi tạo một phương thức sửa đổi Layout
hoặc layout
tuỳ chỉnh, hàm đo lường nội tại sẽ được tự động tính toán dựa trên giá trị gần đúng. Do đó, cách tính có thể không chính xác cho tất cả bố cục. Các API này có các tuỳ chọn để ghi đè những giá trị mặc định như vậy.
Để chỉ định các phép đo hàm nội tại của Layout
tuỳ chỉnh, hãy ghi đè minIntrinsicWidth
, minIntrinsicHeight
, maxIntrinsicWidth
và maxIntrinsicHeight
của giao diện MeasurePolicy
khi tạo.
@Composable fun MyCustomComposable( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( content = content, modifier = modifier, measurePolicy = object : MeasurePolicy { override fun MeasureScope.measure( measurables: List<Measurable>, constraints: Constraints ): MeasureResult { // Measure and layout here // ... } override fun IntrinsicMeasureScope.minIntrinsicWidth( measurables: List<IntrinsicMeasurable>, height: Int ): Int { // Logic here // ... } // Other intrinsics related methods have a default value, // you can override only the methods that you need. } ) }
Khi tạo phương thức sửa đổi layout
tuỳ chỉnh, hãy ghi đè các phương thức có liên quan trong giao diện LayoutModifier
.
fun Modifier.myCustomModifier(/* ... */) = this then object : LayoutModifier { override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { // Measure and layout here // ... } override fun IntrinsicMeasureScope.minIntrinsicWidth( measurable: IntrinsicMeasurable, height: Int ): Int { // Logic here // ... } // Other intrinsics related methods have a default value, // you can override only the methods that you need. }
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Bố cục tuỳ chỉnh {:#custom-layouts }
- Dòng căn chỉnh trong Jetpack Compose
- Các giai đoạn Jetpack Compose