Skip to content

⚙ [트러블 슈팅] Chip with TextField

Yongsu Lim edited this page Dec 2, 2024 · 1 revision

⚠️ 문제 상황1

chipwithtextfield

이 component는 chip의 역할을 하는 component입니다. chip 내부에서 이 칩의 text가 변경이 되어야 하고, X를 눌렀을 때 이 Chip이 삭제되어야 합니다.

기존 입력이 필요하지 않았을 때는 다음과 같이 component를 구성하였습니다.

Box(
    modifier = Modifier.height(32.dp),
    contentAlignment = Alignment.Center
) {
    InputChip(
        selected = false,
        onClick = onRemove,
        label = {
            Text(
                text = item,
                style = MaterialTheme.typography.labelLarge,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        },
        trailingIcon = {
            Icon(
                modifier = Modifier.size(18.dp),
                imageVector = Icons.Outlined.Clear,
                contentDescription = "trailing icon"
            )
        }
    )
}

AddButton과 같은 크기를 유지하기 위해서 Box를 활용하였고, InputChip을 통해서 label과 trailingIcon을 설정하여 쉽게 구현하였습니다.

이 상태에서 입력이 가능하게 만들기 위해서 label 부분에 TextField 를 넣어주었습니다. 그 결과 TextFieldValue 가 보이지 않는 버그가 발생하였습니다.

❓ 문제의 이유1

이를 정확하게 테스트 하기 위해서 Box의 height를 32.dp~56.dp까지 실험하였습니다.

이와 같은 버그가 발생하는 이유는 TextField의 내부 코드를 확인하면 BasicTextField 를 사용하여 TextField의 핵심 동작을 처리하고, TextFieldDecorationBox를 활용하여 스타일을 추가하고 있습니다. 그리고 TextField는 Material Design 가이드에 따라 defaultMinSize 로 최소 높이를 56.dp로 설정하고 있습니다. (추가로 MinWidth는 280.dp로 설정하고 있습니다.)

또, TextField는 내부적으로 Label, Text, Placeholder, Error, Icon 등을 포함하고 있고 이 요소들을 적절하게 표시하기 위해서 일정한 공간을 확보하고 있습니다. 따라서 height를 강제로 줄이게 된다면 TextField의 content가 겹치거나 clip 될 수 있습니다.

✔️ 해결1

디자인에서 height를 32.dp로 설정하기 때문에 디자인을 지키면서 component를 구현하기 위해서 BasicTextField 를 사용하였습니다.

BasicTextField는 Jetpack Compose에서 제공하는 기본적인 텍스트 입력 필드입니다. TextField보다 더 저수준의 구현을 제공합니다. TextField와 달리 기본적인 스타일링이나 레이아웃 요소들을 제공하지 않으며, 커스터마이징에 더 유연하게 사용될 수 있습니다.

정리하면 BasicTextField 는 기본적으로 텍스트를 입력받는 기능만 제공하는 component입니다.

⚠️ 문제 상황2

BasicTextField(
    value = item,
    onValueChange = {
        if (it.length <= 10) {
            onEdit(it)
        }
    },
    textStyle = MaterialTheme.typography.labelLarge.copy(color = MaterialTheme.colorScheme.onSurfaceVariant),
    modifier = Modifier.wrapContentWidth()
)

이제 위와 같이 설정하였으나 또 다른 문제가 발생하였습니다.

BasicTextField의 넓이가 글자의 길이에 맞게 설정이 되어야 하는데, 기본 넓이가 설정되어 있다는 점이었습니다.

❓ 문제의 이유2

이는 BasicTextField 내부 코드의 TextFieldTextLayoutModifier , textFieldMinSize 에서 textStyle에 기반하여 기본 너비를 설정하고 있기 때문입니다.

정확하게는 heightInLines 에서 fontSize를 기반으로 최소 1줄 이상의 높이를 보장하고, textFieldMinSize 에서 textStyle의 fontSize, letterSpacing 을 기반으로, 한 글자가 차지하는 기본 크기를 계산하고 최소 몇 글자가(한 줄) 담길 수 있는 너비와 높이를 강제합니다. TextFieldTextLayoutModifier 는 위와 같은 정보를 기반으로 레이아웃을 계산합니다.

✔️ 해결2

IntrinsicSize 적용하여 해결하였습니다.

IntrinsicSize 는 composable의 크기를 내부 콘텐츠의 내제된 크기(Intrinsic Size)에 맞추도록 강제하는 속성입니다. 콘텐츠의 내용과 스타일에 따라 컴포저블이 얼마나 커질 수 있는지 얼마나 작아질 수 있는지 결정합니다.

IntrinsicSize.Min 을 사용하면 내부 콘텐츠의 최소 사이즈로 강제 지정합니다. 예를 들어서 32.dp 의 너비를 가진 composable과 1.dp를 가진 composable이 자식 composable로 존재한다고 할 때, 부모 composable에 Modifier.width(IntrinsicSize.Min) 을 지정하면 부모 composable의 너비가 1.dp로 지정이 됩니다.

BasicTextField에 적용하면 텍스트의 크기가 없으면 최소 크기 요구사항이 0.dp로 처리됩니다. 따라서 레이아웃 크기를 0.dp로 설정할 수 있기 떄문에 원하는 동작을 수행하도록 구현할 수 있었습니다.

Clone this wiki locally