🙆
jetpack composeで、textにoverflowを設定しつつ拡張子だけ表示する
用途としては、こんな感じ。
ファイル名をText composableで表示するときに、普通にtextにoverflowを設定すると最後が省略されて拡張子が見えなくなるので、なんとかしようという話。
参考
色々カスタマイズの余地はある。
@Composable
fun EllipsisTextWithExtension(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current,
) {
var textLayoutResultState by remember(text) {
mutableStateOf<TextLayoutResult?>(null)
}
var extensionWidth: Float by remember { mutableFloatStateOf(0f) }
var maxLineString: String by remember { mutableStateOf("") }
var maxLineStringWidth: Float by remember { mutableFloatStateOf(0f) }
var counter: Int by remember { mutableIntStateOf(0) }
SubcomposeLayout(modifier) { constraints ->
subcompose("measure") {
Text(
text = text,
modifier = modifier,
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
softWrap = softWrap,
maxLines = Int.MAX_VALUE,
onTextLayout = {
textLayoutResultState = it
},
style = style,
)
}.first().measure(Constraints())
val textLayoutResult = textLayoutResultState
?: return@SubcomposeLayout layout(0, 0) {}
val placeable = subcompose("visible") {
val finalText = remember(text, textLayoutResult, constraints.maxWidth) {
val lineCount = textLayoutResult.lineCount
if (lineCount <= maxLines) {
return@remember text
} else {
try {
val maxLineStartOffset = textLayoutResult.getLineStart(maxLines - 1)
val maxLineEndOffset = textLayoutResult.getLineEnd(maxLines - 1, true)
val lastLineEndOffset = textLayoutResult.getLineEnd(lineCount - 1, true)
val periodOffset = text.indexOf(".")
val extension = text.substring(periodOffset, lastLineEndOffset)
for (i in periodOffset until lastLineEndOffset) {
extensionWidth += textLayoutResult.getBoundingBox(i).width
}
val ellipsisWidth = textLayoutResult.getBoundingBox(periodOffset).width * 3
val availableWidth = constraints.maxWidth
while (availableWidth - maxLineStringWidth - ellipsisWidth - extensionWidth > 0) {
maxLineString = text.substring(maxLineStartOffset, maxLineStartOffset + counter)
maxLineStringWidth += textLayoutResult.getBoundingBox(maxLineStartOffset + counter).width
counter ++
if (maxLineStartOffset + counter > maxLineEndOffset) {
throw IllegalStateException("The limit of chars that can be retrieved on this line has been exceeded.")
}
}
text.substring(0, maxLineStartOffset) + maxLineString + "..." + extension
} catch (exception: Exception) {
exception.stackTrace
text
}
}
}
Text(
text = finalText,
modifier = modifier,
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
softWrap = softWrap,
maxLines = maxLines,
onTextLayout = onTextLayout,
style = style,
)
}[0].measure(constraints)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
}
}
Discussion