内容
コンテンツをリスト表示する際に、記事などの長文を部分表示したい場合に maxLines
パラメータを使って表示を制限するが、現在どこまで文字列が表示されているかを知りたい。どうすればよいか。
対応
TextViewの場合にはTextUtils.ellipsize()
という関数に文字列と画面幅を渡してゴニョゴニョとやっていたが、Jetpack Composeの場合にはTextLayoutResult
が利用できる。
Text(text = longText, overflow = TextOverflow.Ellipsis, maxLines = 3, onTextLayout = { textLayoutResult -> if (textLayoutResult.hasVisualOverflow) { overflowOffset = it.getLineEnd(2, true) } })
onTextLayout
にCompose後のレイアウト結果を表すTextLayoutResult
のインスタンスが渡されてくるので、hasVisualOverflow
で表示されなかった部分があるかどうかを確認する。getLineEnd(line:Int)
でその行の最後の文字位置が得られる。第2パラメータは結果を目に見えるもののみに限定するかどうか。overflow = TextOverflow.Ellipsis
にした場合にここがfalse
だと全テキストが最後の行に含まれてしまい、期待した値が得られない。また、hasVisualOverflow
の代わりに isLineEllipsized()
を使うこともできるが、その場合はoverflow = TextOverflow.Clip
の場合に結果がfalse
になってしまうので注意
/** 3行以上の文章を上下に分割する例 **/ @Composable fun OverflowText(longText: String) { var overflowOffset: Int by remember { mutableStateOf(0) } Column { // 上から3行 Text(text = longText, overflow = TextOverflow.Clip, maxLines = 3, onTextLayout = { if (it.hasVisualOverflow) { overflowOffset = it.getLineEnd(2, true) } }) Spacer(modifier = Modifier.height(32.dp)) if (overflowOffset > 0) { // 溢れた部分 Text(text = longText.substring(overflowOffset)) } } }
結果の例
前よりも楽になってるんだけど、getLineEnd
で目的が達成できることを見つけるまでに時間がかかった。