Compose 규칙 중 하나는 하위 요소를 한 번만 측정해야 한다는 것입니다. 하위 요소를 두 번 측정하면 런타임 예외가 발생합니다. 하지만 하위 요소를 측정하기 전에 하위 요소에 관한 정보가 필요한 경우도 있습니다.
내장 기능을 사용하면 하위 요소가 실제로 측정되기 전에 하위 요소를 쿼리할 수 있습니다.
컴포저블에 intrinsicWidth
또는 intrinsicHeight
를 요청할 수 있습니다.
(min|max)IntrinsicWidth
: 이 높이에서 콘텐츠를 적절하게 그릴 수 있는 최소/최대 너비는 무엇인가요?(min|max)IntrinsicHeight
: 이 너비에서 콘텐츠를 적절하게 그릴 수 있는 최소/최대 높이는 무엇인가요?
예를 들어 width
가 무한대인 Text
의 minIntrinsicHeight
를 요청하면 텍스트가 한 줄에 그려진 것처럼 Text
의 height
가 반환됩니다.
내장 기능 실제 사례
다음과 같이 화면에 구분선으로 구분된 두 텍스트를 표시하는 컴포저블을 만든다고 가정해 보겠습니다.
이렇게 하려면 어떻게 해야 하나요? 안에 두 Text
가 있고 최대한 확장할 수 있으며 중앙에 Divider
가 있는 Row
를 만들 수 있습니다. 구분선을 가장 높은 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입니다. Text
minIntrinsicHeight
는 특정 width
가 지정된 경우 텍스트의 높이입니다. 따라서 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.
})