Checkbox 아이콘을 텍스트의 원하는 위치로 정렬 하는 방법 (Jetpack Compose)

Youngjik Yoon
PRND
Published in
6 min readAug 17, 2023

- 텍스트 가운데에 아이콘 정렬 쉽게 하는 방법
- 텍스트 첫 번째 줄, 마지막 줄에 아이콘 정렬 쉽게 하는 방법
- 아무리 찾아도 Stack Overflow에 없는 내용?

안녕하세요.
헤이딜러 안드로이드팀의 윤영직입니다.

🎉 드디어 헤이딜러에 Jetpack Compose가 도입되었습니다. 🎉
앞으로 Jetapck Compose를 사용한 문제 해결 사례를 공유 할 예정입니다.

UI를 구현 하다보면 위와 같은 요구사항을 자주 마주하게 됩니다.
그러면 우리는 간단한 코드로 이 기능을 구현할 수 있습니다.

Row(verticalAlignment = Alignment.CenterVertically) {
Icon(...)
Text(...)
}

하지만 안드로이드 개발자라면 반드시 이 질문을 하게 됩니다.
*혹은 QA에서 잡힙니다* 😢

“텍스트가 길어서 두 줄이 될 것 같은데요? ”

물론 대부분의 기기에서는 문제가 되지 않습니다. 😃
하지만 최근 폴더블 디바이스가 늘어남에 따라 접혔을 때의 가로 크기 280dp를 고려해야 합니다.

디자이너에게 “텍스트가 두 줄이 되면 어떻게 할까요?” 로 물어본다면 보통은 아래의 답변 중 하나가 됩니다.

  1. 그대로 가운데 정렬 해주세요.
  2. 텍스트 문구를 조금 수정할게요.
  3. 텍스트의 첫 번째 줄에 가운데에 정렬 시켜주세요.

그동안 우리는 이 문제를 해결하기 위해 단순한 방법을 사용 해 왔습니다.

Row {
Icon(Modifier.padding(top = 12.dp))
Text(Modifier.padding(vertical = 12.dp))
}

이 코드는 잘 동작합니다. 하지만 매우 불편합니다.

텍스트 스타일이 변경되면 어떻게 하지?
아이콘이 변경되면 어떻게 하지?
텍스트의 첫 번째 줄이 아닌 두 번째 줄에 위치해야 한다고!?

하드코딩을 사용한 코드는 버그를 발생 시키기 때문에
매번 디자인을 보고 padding을 변경하고 싶지 않았습니다.

Alignment

Row에서는 정렬 선을 사용해 Content의 위치를 결정합니다.
따라서 단순하게 생각한다면, 정렬 선을 텍스트의 가운데에 두면 됩니다.

아이콘을 정렬하는 기준을 정의하면 다음과 같습니다.

  1. 텍스트가 그려지지 않았다면 ➡️ 가운데 정렬
  2. 텍스트가 한 줄 이라면 ➡️ 가운데 정렬
  3. 텍스트가 두 줄 이상이라면 ➡️ 첫 번째 줄에 정렬

이 문제를 해결하기 위해 Text에서 제공하는 TextLayoutResult를 사용 할 수 있습니다.

먼저, TextLayoutResult를 가져오기 위해 remember를 사용합니다.

var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
Text(
onTextLayout = { textLayoutResult = it },
)

다음으로 위 기준에 따라 Alignment.Vertical을 구현하는 코드를 작성합니다.

private fun textCenterAlignment(
textLayoutResult: TextLayoutResult?,
lineCount: Int,
): Alignment.Vertical = Alignment.Vertical { size, space ->
val height = space - size
val center = (height / 2f).roundToInt() // Alignment.CenterVertically 참고
when {
textLayoutResult == null -> center // 텍스트가 그려지지 않았다면
textLayoutResult.lineCount <= 1 -> center // 텍스트가 한 줄 이라면
else -> { // 텍스트가 두 줄 이상이라면
val textHeight = textLayoutResult.size.height
val lineTop = textLayoutResult.getLineTop(lineCount)
val lineBottom = textLayoutResult.getLineBottom(lineCount)
val lineCenter = lineTop + (lineBottom - lineTop) / 2
(height - textHeight) / 2 + lineCenter.roundToInt()
}
}
}

이렇게 정의한 정렬 선을 Row의 아이콘에 배치할 수 있습니다.

var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
val alignment = remember(textLayoutResult) {
textCenterAlignment(textLayoutResult, lineCount = 0)
}
Row(
modifier = Modifier.then(modifier),
) {
Icon(
modifier = Modifier.align(alignment)
)
Text(
modifier = Modifier.align(Alignment.CenterVertically),
onTextLayout = { textLayoutResult = it },
)
}
}

지금까지 정렬 선을 이용해 아이콘을 원하는 위치에 정렬하는 방법을 알아보았습니다.

TextLayoutResult와 Alignment를 사용하면 TextView에서는 시도하지 못한 다양한 구현을 할 수 있습니다. 🎉

저희와 함께 헤이딜러 서비스를 발전 시켜나가실 분들을 기다리고 있습니다.
🎉 헤이딜러 안드로이드팀은 올해부터 Jetpack Compose와 함께합니다 🎉

감사합니다.

--

--