2021年5月27日木曜日

Jetpack Compose で AutoSizeableTextView 的なのを作る

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

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





0 件のコメント:

コメントを投稿