Reputation: 9814
I'm trying to create a mentions feature in Jetpack Compose. I found a library which uses an EditText and I of course could use that, but I really want to create this in Jetpack Compose. The only problem is, in some cases it's hard to keep track of the changes to the text. For example if a person moves the cursor to a word and the keyboard shows suggestions and the user clicks it. When using EditText you can use beforeTextChanged
and onTextChanged
and it tells the start of the change, the length before the change and the length after the change.
So my question is, is there a somewhat equal method for Jetpack Compose TextField or a way to get these values?
Upvotes: 1
Views: 5827
Reputation: 9814
After trying some possibilities I feel like I found the way to get the diff of which text has changed when typing in a TextField
in Jetpack Compose. I will explain it with some code and also provide the full source if you want to use mentions in Jetpack Compose.
First thing we have to do is find the first difference between two strings. I use this code:
// Find first difference between two strings
private fun String.indexOfDifference(otherString: String): Int {
if (this.contentEquals(otherString)) {
return -1
}
for (i in 0 until min(this.length, otherString.length)) {
if (this[i] != otherString[i]) {
return i
}
}
if (this.length != otherString.length) {
return min(this.length, otherString.length)
}
return -1
}
One fault of this code, which for me doesn't really matter is if there are multiple of the same characters in a row. For example if you have a space and add a space before that space, it thinks the change happened at the second space instead of the first.
After knowing the first difference of the text we need to know the length of the diff in the old and new string:
// Go through the text and find where the texts differentiate
private fun getDiffRange(indexOfDiffStart: Int, oldText: String, newText: String): Pair<IntRange, IntRange> {
val newLastIndex = max(0, newText.length)
val newStartIndex = min(indexOfDiffStart, newLastIndex)
val oldLastIndex = max(0, oldText.length)
val oldStartIndex = min(indexOfDiffStart, oldLastIndex)
var loopIndex = oldStartIndex
var oldTextIndex = -1
while(loopIndex <= oldLastIndex) {
// From where texts differentiates, loop through old text to find at what index the texts will be the same again
oldTextIndex = newText.indexOf(oldText.substring(loopIndex, oldLastIndex))
if(oldTextIndex >= 0) {
break
}
loopIndex++
}
if(oldTextIndex >= 0) {
return Pair(first = oldStartIndex .. loopIndex, second = newStartIndex .. max(0, oldTextIndex))
}
return Pair(first = oldStartIndex .. oldLastIndex, second = newStartIndex .. newLastIndex)
}
The method above will loop through the old text until we can find the entire remaining of the old text in the new text. This way we know the location and length of what has changed in the old text and also immediately know the same for the new text.
In the onValueChange
method of the TextField
you can check for the first difference:
if (oldTextFieldValue.text.contentEquals(newTextFieldValue.text)) {
// Content stayed the same, probably cursor change
} else {
val indexOfDiff = oldTextFieldValue.text.indexOfDifference(newTextFieldValue.text)
if (indexOfDiff >= 0) {
val (oldDiffRange, newDiffRange) = getDiffRange(indexOfDiff, oldTextFieldValue.text, newTextFieldValue.text)
}
}
This was the information I needed to handle the mentions. Maybe this already helps somebody that has the same problem. But if you want to see my full mentions implementation you can find it here: https://gist.github.com/kevinvanmierlo/4bd011479c66eed598852ffeacdc0156
Upvotes: 0
Reputation: 363905
You can use the onValueChange
property:
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = {
// text= oldValue
// it = newValue
}
)
Upvotes: 1