I am trying to create a single line text input in Jetpack Compose.
Since this input should have a fix width of 200.dp and is only one line, longer text is going to be cut off. Sometimes it looks like the cut off text does not even exist because the cut is made between two letters. In order to show the user that there is "more" text already typed I would prefer an ellipsis (e.g. This is a sample inpu...) effect.
I tried to use the default TextField and BasicTextField composables but there seems to be no easy solution.
Is there a way to create this ellipsis effect in Jetpack Compose?
Although the solution provided by Brian Ngure is not working, because wrong offsets trigger crashes. I used his idea to create a working one (at least for me :D).
VisualTransformation { text ->
TransformedText(buildAnnotatedString {
if (maxSymbols < 3) {
append(text)
} else {
append(text.take(maxSymbols - 3))
append("...")
}
}, object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return if (maxSymbols < 3) text.length else maxSymbols
}
override fun transformedToOriginal(offset: Int): Int {
return text.length
}
})
Where 3 == "...".length
In order for that to work you have to get maxSymbols that can be drawn on the screen.
You can do that in the onTextLayout callback of the TextField
There are ready to use variables didOverflowWidth, didOverflowHeight, hasVisualOverflow
onTextLayout = { result ->
isTextOverflow = result.didOverflowWidth
},
If that's not working, you can get information about current text field width result.size.width and by comparing that with the screen width from LocalContext.current.resources.displayMetrics.widthPixels you can understand when there is an overflow. For some reason the dedicated variables are not working for me.
When the overflow happens, just use another variable to remember maxSymbols
and apply this transformation to the TextField and a decoration box, if you use one.
var isTextOverflow by remember { mutableStateOf(false) }
var maxSymbols: Int = remember(isTextOverflow) { if (isTextOverflow) text.text.length else 0 }
And also you might need to recreate the transformation as different symbols have different width and for different texts width might be different, but recreating transformation too often can be bad for performance.
Update: You might also find useful a TextMeasurer object that allows you to compare textField width with the text width if your textfield is not screen wide
val textMeasurer = rememberTextMeasurer()
val measuredTextWidth = textMeasurer.measure(
text = text,
style = textStyle
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With