Last active
December 14, 2023 04:00
-
-
Save warahiko/573748442a331a6244853570f0abe755 to your computer and use it in GitHub Desktop.
Sample of auto-sizable Text composable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun AutoSizableText( | |
text: String, | |
modifier: Modifier = Modifier, | |
minFontSize: TextUnit = 12.sp, | |
maxFontSize: TextUnit = 112.sp, | |
granularityInPx: Int = 1, | |
) { | |
val autoSizer = rememberAutoSizer(minFontSize, maxFontSize, granularityInPx) | |
val style = LocalTextStyle.current | |
BoxWithConstraints(modifier = modifier) { | |
val fontSize = remember(text, autoSizer, style, constraints) { | |
autoSizer.autoSize( | |
text = text, | |
style = style, | |
constraints = constraints, | |
overflow = TextOverflow.Ellipsis, | |
) | |
} | |
Text( | |
text = text, | |
fontSize = fontSize, | |
overflow = TextOverflow.Ellipsis, | |
style = style, | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AutoSizer( | |
minFontSize: TextUnit, | |
maxFontSize: TextUnit, | |
granularityInPx: Int, | |
density: Density, | |
private val textMeasurer: TextMeasurer, | |
) { | |
// 候補となるフォントサイズのリスト | |
// 二分探索できるように順に並べる | |
private val fontSizes: List<TextUnit> = generateSequence(minFontSize) { previous -> | |
with(density) { | |
(previous.toPx() + granularityInPx).toSp() | |
} | |
} | |
.takeWhile { it < maxFontSize } | |
.plus(maxFontSize) | |
.toList() | |
fun autoSize( | |
text: String, | |
style: TextStyle, | |
constraints: Constraints, | |
overflow: TextOverflow, | |
): TextUnit { | |
// 二分探索で最適なフォントサイズを取得する | |
val index = fontSizes.binarySearch { targetFontSize -> | |
val result = textMeasurer.measure( | |
text = text, | |
style = style.copy(fontSize = targetFontSize), | |
constraints = constraints, | |
overflow = overflow, | |
) | |
// はみ出していれば正、そうでなければ負を返すと、 | |
// 「最適なフォントサイズの『inverted insertion point』」が得られる | |
// see: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/binary-search.html | |
if (result.hasVisualOverflow) 1 else -1 | |
} | |
assert(index < 0) | |
val insertionPoint = -(index + 1) | |
// 最適なフォントサイズの挿入位置は、はみ出すフォントサイズのうち最小のものの位置に等しいので、 | |
// その一つ前を返す | |
val resultIndex = (insertionPoint - 1).coerceAtLeast(0) | |
return fontSizes[resultIndex] | |
} | |
} | |
@Composable | |
fun rememberAutoSizer( | |
minFontSize: TextUnit, | |
maxFontSize: TextUnit, | |
granularityInPx: Int, | |
): AutoSizer { | |
val density = LocalDensity.current | |
val textMeasurer = rememberTextMeasurer() | |
return remember(minFontSize, maxFontSize, granularityInPx, density, textMeasurer) { | |
AutoSizer(minFontSize, maxFontSize, granularityInPx, density, textMeasurer) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun SimpleAutoSizableText( | |
text: String, | |
modifier: Modifier = Modifier, | |
minFontSize: TextUnit = 12.sp, | |
maxFontSize: TextUnit = 112.sp, | |
granularityInPx: Int = 1, | |
) { | |
val density = LocalDensity.current | |
var fontSize by remember(text, minFontSize, maxFontSize, granularityInPx, density) { | |
mutableStateOf(maxFontSize) | |
} | |
Text( | |
text = text, | |
fontSize = fontSize, | |
overflow = TextOverflow.Ellipsis, | |
modifier = modifier, | |
onTextLayout = { textLayoutResult -> | |
if (textLayoutResult.hasVisualOverflow && fontSize > minFontSize) { | |
fontSize = with(density) { | |
(fontSize.toPx() - granularityInPx).toSp() | |
} | |
} | |
} | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment