Compose 的其中一項規則是只能測量子項一次;如果測量子項兩次,系統將擲回執行階段例外狀況。不過有時候,您必須先掌握子項的某些相關資訊,才能進行測量。
內建函式可讓您在實際測量前查詢子項相關資訊。
如果是可組合項,您可以查詢其 intrinsicWidth
或 intrinsicHeight
:
(min|max)IntrinsicWidth
:依據這個高度,您可以正確繪製內容的最小/最大寬度為何?(min|max)IntrinsicHeight
:依據這個寬度,您可以正確繪製內容的最小/最大高度為何?
舉例來說,如果在 width
設為無限的情況下查詢 Text
的 minIntrinsicHeight
,系統會將文字視為繪製在單一直線上並傳回 Text
的 height
。
內建函式實際使用狀況
假設我們要建立一個可組合項,在畫面上顯示兩個文字元素並以分隔線隔開,如下所示:
我們該怎麼做?我們可以設定一個 Row
,在當中加入兩個 Text
並盡可能擴大兩者之間的距離,然後在中間加入一個 Divider
。我們將分隔線的高度設成 Text
的最大高度,寬度則為細 (width = 1.dp
)。
@Composable
fun TwoTexts(
text1: String,
text2: String,
modifier: Modifier = Modifier
) {
Row(modifier = modifier) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.Start),
text = text1
)
Divider(
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")
}
}
}
在預覽畫面中,我們發現分隔線擴展至整個畫面,並不符合我們的預期:
之所以會發生這種情況,原因是 Row
會個別測量每個子項,而 Text
的高度無法用於限制 Divider
。我們想讓 Divider
填滿指定高度的可用空間。想達到這個目的,我們可以使用 height(IntrinsicSize.Min)
修飾符。
height(IntrinsicSize.Min)
會將子項的高度強制調整為內建函式的最低高度。由於該修飾符有遞迴性,因此會查詢 Row
和其子項的 minIntrinsicHeight
。
套用到我們的程式碼後,就能產生我們預期的結果:
@Composable
fun TwoTexts(
text1: String,
text2: String,
modifier: Modifier = Modifier
) {
Row(modifier = modifier.height(IntrinsicSize.Min)) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = 4.dp)
.wrapContentWidth(Alignment.Start),
text = text1
)
Divider(
color = Color.Black,
modifier = Modifier.fillMaxHeight().width(1.dp)
)
Text(
modifier = Modifier
.weight(1f)
.padding(end = 4.dp)
.wrapContentWidth(Alignment.End),
text = text2
)
}
}
預覽如下:
Row
可組合項的 minIntrinsicHeight
將是其子項的 minIntrinsicHeight
上限。由於 Divider
元素在沒有限制條件的情況下不會占用空間,因此其 minIntrinsicHeight
為 0,而在指定特定 width
的情況下,Text
minIntrinsicHeight
將為文字的高度。因此,Row
元素的 height
限制將是 Text
的 minIntrinsicHeight
上限。Divider
隨即會將其 height
擴展至 Row
指定的 height
限制。
自訂版面配置中的內建函式
建立自訂 Layout
或 layout
修飾符時,系統會根據估計值自動計算內建函式測量結果。因此,這些計算結果的正確性會依版面配置而異。這些 API 提供了覆寫這些預設值的選項。
如要指定自訂 Layout
的內建函式測量資料,請在建立MeasurePolicy
介面時覆寫 minIntrinsicWidth
、minIntrinsicHeight
、maxIntrinsicWidth
和 maxIntrinsicHeight
。
@Composable
fun MyCustomLayout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier
) {
Layout(
modifier = modifier,
content = content,
measurePolicy = object : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
// Measure and layout here
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
// Logic for calculating custom maxIntrinsicHeight here
}
// Other intrinsics related methods have a default value,
// you can override only the methods that you need.
}
)
}
建立自訂 layout
修飾符時,請在 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.
})