Reputation: 203
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?
Upvotes: 4
Views: 3915
Reputation: 1659
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
)
Upvotes: 1
Reputation: 11
I realise this is old, but I faced the same predicament. This is my solution.
@Composable
fun ellipsisVisualTransformation() = VisualTransformation { text ->
val ellipsis = "..."
val maxLength = 20 // Set your desired max length
if (text.length <= maxLength) {
TransformedText(text = text, offsetMapping = OffsetMapping.Identity)
} else {
TransformedText(
AnnotatedString.Builder().apply {
append(text.take(maxLength - ellipsis.length))
append(ellipsis)
}.toAnnotatedString(),
OffsetMapping.Identity
)
}
}
And in my OutlinedTextField, I added this:
visualTransformation = ellipsisVisualTransformation()
Upvotes: 0