2021年5月27日木曜日

Jetpack Compose で AutoSizeableTextView 的なのを作る

残念ながらオフィシャルでのサポートは(まだ)ない。

Text の onTextLayout で TextLayoutResult の hasVisualOverflow (didOverflowWidth および didOverflowHeight もある) が true だったら remember {} してる文字サイズを変更するという方法でそれっぽいのは作れるが、State が変わる回数が多い(最終的な textSize と maxTextSize の差が大きい)と文字サイズが変わるアニメーションみたいになってしまうのがつらい。 @Composable fun AutoSizeableText( text: String, maxTextSize: Int = 16, minTextSize: Int = 14, modifier: Modifier ) { var textSize by remember(text) { mutableStateOf(maxTextSize) } Text( text = text, fontSize = textSize.sp, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = modifier, onTextLayout = { if (it.hasVisualOverflow && textSize > minTextSize) { textSize -= 1 } } ) }
↑だと1文字増減したときに maxTextSize からやり直しになるので、現在の文字サイズを覚えておいてそこから +/- するようにしたのが ↓ @Composable fun AutoSizeableText( text: String, maxTextSize: Int = 16, minTextSize: Int = 14, modifier: Modifier ) { var textSize by remember { mutableStateOf(maxTextSize) } val checked = remember(text) { mutableMapOf<Int, Boolean?>() } var overflow by remember { mutableStateOf(TextOverflow.Clip) } Text( text = text, fontSize = textSize.sp, maxLines = 1, overflow = overflow, modifier = modifier, onTextLayout = { if (it.hasVisualOverflow) { checked[textSize] = true if (textSize > minTextSize) { textSize -= 1 } else { overflow = TextOverflow.Ellipsis } } else { checked[textSize] = false if (textSize < maxTextSize) { if (checked[textSize + 1] == null) { textSize += 1 } } } } ) }
それでも State が変わる回数が多い(maxTextSize と minTextSize の差が大きくて、一度に長い文字をペーストするとか)と文字サイズが変わるアニメーションみたいになってしまうのがつらいんですよね〜。





0 件のコメント:

コメントを投稿