컴포즈의 규칙중 하나는 하위 요소를 한번만 측정해야 한다는 것이다. 하위요소를 두번 측정하는 것은 런타임 예외를 발생시킨다. 하지만 때로는 측정하기 전에 하위요소에 대한 몇가지 정보가 필요하다.

Intrinsics를 사용하면 실제로 측정되기 전에 하위요소에 질의할 수 있다.

개발자는 intrinsicWidth 또는 intrinsicHeight를 컴포저블에게 요청할 수 있다.

  • (min|max)IntrinsicWidth: 높이(height)가 주어지고, 적절히 콘텐츠를 그릴 수 있는 최소/최대 너비
  • (min|max)IntrinsicHeight: 너비(width)가 주어지고, 적절히 콘텐츠를 그릴 수 있는 최소/최대 너비

예를 들어, 무한한 너비의 텍스트에 minIntrinsicHeight를 요청하면 텍스트가 한 줄에 그려진 것처럼 텍스트의 높이가 반환된다.

실전 Intrinsics

다음과 같이 구분선으로 분리된 화면에 두 개의 텍스트를 표시하는 컴포저블을 만들고 싶다고 상상해보자.

어떻게 하면 될까? 가능한 한 많이 확장되는 두 개의 텍스트가 있는 Row(행)과 그 중간에 Divider(구분선)을 가질 수 있다. 우리는 Divider가 Text의 높이만큼 길고 너비는 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
        )

        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() {
    LayoutsCodelabTheme {
        Surface {
            TwoTexts(text1 = "Hi", text2 = "there")
        }
    }
}

미리보기로 보면, Divider가 전체 스크린 영역까지 확장되어있는 것을 볼 수 있다. 이건 우리가 원하는게 아닌데…

이런일이 발생하는 원인은 Row 각 하위요소를 독립적으로 측정하고 Text의 높이 Divider를 제한하기 위해 사용되지 않는다. 우리가 원하는 것은 주어진 높이 내에서 Divider가 가능한 공간을 모두 채우는 것이다. 이를 위해, height(IntrinsicSize.Min) modifier를 사용할 수 있다.

height(IntrinsicSize.Min)는 최소한의 고유한 높이만큼 커지도록 강제되는 하위요소의 사이즈를 조정한다. 이는 재귀적이므로, Row와 하위요소 minIntrinsicHeight에 질의한다.

위의 내용을 코드에 적용하면, 예상대로 작동하게 된다.

@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
        )

        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() {
    LayoutsCodelabTheme {
        Surface {
            TwoTexts(text1 = "Hi", text2 = "there")
        }
    }
}

미리보기로 보면 다음과 같다.

RowminIntrinsicHeight는 하위요소의 최대 minIntrinsicHeight가 된다. Divider에 제약조건이 주어지지 않으면 공간을 차지 하지 않으므로, DividerminIntrinsicHeight는 0이다. Text의 minIntrinsicHeight는 지정된 너비의 텍스트 높이가 된다. 그러므로 Rowheight 제약조건은 Text들 중 최대치의 minIntrinsicHeight가 된다. Divider는 그런뒤 주어진 Rowheight에 맞게 높이를 확장시킨다.

DIY

커스텀 레이아웃을 만들때마다, MeasurePolicy 인터페이스의 (min|max)Intrinsic(Width|Height)와 함께 intrinsics가 어떻게 계산되는지 수정할 수 있다. 그러나 수정하지 않고 보통의 경우 기본값이면 충분하다.

또한 좋은 기본값을 가지고 있는 Modifier 인터페이스Density.(min|max)Intrinsic(Width|Height)Of 메서드를 재정의한 것을 사용하여 intrinsics를 수정할 수도 있다.

카테고리: Compose

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.