일을 하던 도중 Text를 custom 해서 사용할 일이 있었다.
maxLine = 2이며, text가 영역을 넘어서면 크기를 2씩 줄여나가야 했다.
xml이었다면 autosizeText 속성을 사용하여 간편하게 사용할 수 있었겠지만, 현재 나는 Compose를 사용하고 있기에 Text Component를 커스텀하여 사용할 수밖에 없었다.
AutoSizeText의 과정을 간단히 생각해보면
text를 올렸을 때, 길이가 Text Component의 width를 넘어서는지 확인 후, 넘어선다면 글씨 크기를 줄이면 되는 것이었다.
즉, 현재 글씨 크기로 text를 썼을 때, 길이가 넘어서는지를 측정해야 했다.
이를 위해서는 Text Component의 width 값도 함께 알아내야 한다.
첫 번째로 Text Component의 width 값을 알아내보자.
Text(
modifier = Modifier.width(100.dp),
text = "Hello Android"
)
위처럼 width 값을 직접적으로 설정해 준다면 따로 알아낼 필요가 없지만,
Text(
modifier = Modifier.weight(1f),
text = "Hello Android"
)
이런 경우 width 값을 모르기에 측정을 해줘야만 한다.
나는 Text Component의 width 값을 알아내기 위해 BoxConstraints를 사용하였다.
BoxConstraints를 들여다보면 다음과 같다.
@Stable
interface BoxWithConstraintsScope : BoxScope {
/**
* The constraints given by the parent layout in pixels.
*
* Use [minWidth], [maxWidth], [minHeight] or [maxHeight] if you need value in [Dp].
*/
val constraints: Constraints
/**
* The minimum width in [Dp].
*
* @see constraints for the values in pixels.
*/
val minWidth: Dp
/**
* The maximum width in [Dp].
*
* @see constraints for the values in pixels.
*/
val maxWidth: Dp
/**
* The minimum height in [Dp].
*
* @see constraints for the values in pixels.
*/
val minHeight: Dp
/**
* The maximum height in [Dp].
*
* @see constraints for the values in pixels.
*/
val maxHeight: Dp
}
private data class BoxWithConstraintsScopeImpl(
private val density: Density,
override val constraints: Constraints
) : BoxWithConstraintsScope, BoxScope by BoxScopeInstance {
override val minWidth: Dp get() = with(density) { constraints.minWidth.toDp() }
override val maxWidth: Dp get() = with(density) {
if (constraints.hasBoundedWidth) constraints.maxWidth.toDp() else Dp.Infinity
}
override val minHeight: Dp get() = with(density) { constraints.minHeight.toDp() }
override val maxHeight: Dp get() = with(density) {
if (constraints.hasBoundedHeight) constraints.maxHeight.toDp() else Dp.Infinity
}
}
maxWidth가 있는 것을 확인할 수 있고, maxWidth는 자신의 영역에서의 최댓값을 가지게 된다.
즉, Text Component의 width값을 알 수 있게 되는 것이다.
나는 Text의 영역을 알아내기 위해 사용했지만, 다양한 용도로 사용할 수 있을 것 같다는 생각을 하였다.
다음으론 text가 써졌을 때 길이를 측정해야 한다.
따로 영역이 있는 것도 아닌 text의 길이는 또 어떻게 측정해야 하는지 알아보며 rememberTextMeasurer()라는 메서드를 찾아내었다.
@Composable
fun rememberTextMeasurer(
cacheSize: Int = DefaultCacheSize
): TextMeasurer {
val fontFamilyResolver = LocalFontFamilyResolver.current
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
return remember(fontFamilyResolver, density, layoutDirection, cacheSize) {
TextMeasurer(fontFamilyResolver, density, layoutDirection, cacheSize)
}
}
메서드를 열어보면 각각의 Local 정보를 통해 text의 크기를 측정하게 되는 것으로 보이지만, 분석을 제대로 하지 못해 차후 더 알아볼 예정이다.
rememberTextMeasurer의 사용법은 다음과 같다.
val measurer = rememberTextMeasurer()
val density = LocalDensity.current
val textWidth = density.run { measurer.measure(text, textStyle).size.width.toDp() }
var textSize = with(density) { textSizeRange.max.toDp() }
var calWidth: Dp
with(density) {
while(textWidth > maxWidth) {
textSize -= textSizeRange.step.toDp()
calWidth = measurer.measure(text, textStyle.copy(fontSize = textSize.toSp())).size.width.toDp()
}
}
이 글에서는 textSize 변경만을 다루고 있지만, lineHeight, letterSpacing 등 다양한 요소를 변경하며 측정을 할 수도 있다.
이제 areaWidth와 textWidth를 모두 알 수 있으므로 textWidth가 areaWidth보다 작거나 같아지는 textSize를 구할 수 있다.
with(density) {
while(textWidth > maxWidth) {
textSize -= textSizeRange.step.toDp()
textWidth = measurer.measure(text, textStyle.copy(fontSize = textSize.toSp())).size.width.toDp()
}
}
이를 통해 구한 textSize를 TextStyle에 적용하여 사용하면 각 문구에 따라 달라지는 글씨 크기를 확인할 수 있다.
전체 코드 확인
'Android > Compose' 카테고리의 다른 글
[Compose] Compose Text (0) | 2023.07.18 |
---|---|
[Compose] LazyColumn이란? (0) | 2023.07.11 |
[Compose] @Preview 분석 (0) | 2023.06.27 |
[Compose] Compose의 Side-Effect(3) (0) | 2023.05.15 |
[Compose] Compose의 Side-Effect(2) (0) | 2023.05.09 |
댓글