Pour ViewModel, LiveData et liaison de données
J'avais besoin de cette fonctionnalité pour EditText
avec le support multiligne dans mon application notes. Je voulais que le curseur se trouve à la fin du texte lorsque l'utilisateur accède au fragment contenant du texte de note.
La solution proposée par le djleop se rapproche. Mais le problème avec cela est que, si l'utilisateur place le curseur quelque part au milieu du texte à modifier et commence à taper, le curseur sautera à la fin du texte à nouveau. Cela est arrivé parce que leLiveData
émettrait la nouvelle valeur et le curseur sauterait à la fin du texte à nouveau, ce qui empêcherait l'utilisateur de modifier le texte quelque part au milieu.
Pour résoudre ce problème, je l'utilise MediatorLiveData
et lui attribue la longueur d' String
une seule fois à l'aide d'un indicateur. Cela provoquera la lecture de la valeur par LiveData une seule fois, c'est-à-dire lorsque l'utilisateur accède au fragment. Après cela, l'utilisateur peut placer le curseur là où il veut éditer le texte.
ViewModel
private var accessedPosition: Boolean = false
val cursorPosition = MediatorLiveData<Event<Int>>().apply {
addSource(yourObject) { value ->
if(!accessedPosition) {
setValue(Event(yourObject.note.length))
accessedPosition = true
}
}
}
Voici yourObject
une autre LiveData extraite de la base de données qui contient le texte de chaîne que vous affichez dans leEditText
.
MediatorLiveData
Liez ensuite cela à votre EditText à l'aide de l'adaptateur de liaison.
XML
Utilise la liaison de données bidirectionnelle pour afficher le texte ainsi que pour accepter la saisie de texte.
<!-- android:text must be placed before cursorPosition otherwise we'll get IndexOutOfBounds exception-->
<EditText
android:text="@={viewModel.noteText}"
cursorPosition="@{viewModel.cursorPosition}" />
Adaptateur de reliure
@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}
Event
classe
La Event
classe ici est comme un SingleLiveEvent écrit par Jose Alcérreca de Google. Je l'utilise ici pour prendre en charge la rotation de l'écran. L'utilisation du single Event
garantit que le curseur ne sautera pas à la fin du texte lorsque l'utilisateur modifie le texte quelque part au milieu et que l'écran pivote. Il conservera la même position lorsque l'écran pivotera.
Voici la Event
classe:
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
C'est la solution qui fonctionne pour moi et offre une bonne expérience utilisateur. J'espère que cela vous aidera aussi dans vos projets.