Per ViewModel, LiveData e Data binding
Avevo bisogno di questa funzionalità EditText
con il supporto multilinea nella mia app per le note. Volevo il cursore alla fine del testo quando l'utente naviga verso il frammento che ha il testo della nota.
La soluzione suggerita da djleop si avvicina. Ma il problema è che, se l'utente posiziona il cursore da qualche parte nel mezzo del testo per la modifica e inizia a digitare, il cursore salta di nuovo alla fine del testo. Questo è successo perché ilLiveData
emetteva il nuovo valore e il cursore saltava nuovamente alla fine del testo, facendo sì che l'utente non fosse in grado di modificare il testo da qualche parte nel mezzo.
Per risolverlo, lo uso MediatorLiveData
e lo assegno String
solo una volta usando una bandiera. Ciò farà sì che LiveData legga il valore una sola volta, ovvero quando l'utente passa al frammento. Dopodiché l'utente può posizionare il cursore in qualsiasi punto del punto in cui desidera modificare il testo.
ViewModel
private var accessedPosition: Boolean = false
val cursorPosition = MediatorLiveData<Event<Int>>().apply {
addSource(yourObject) { value ->
if(!accessedPosition) {
setValue(Event(yourObject.note.length))
accessedPosition = true
}
}
}
Ecco yourObject
un altro LiveData recuperato dal database che contiene il testo String che si sta visualizzando inEditText
.
Quindi associa questo MediatorLiveData
al tuo EditText usando un adattatore di rilegatura.
XML
Utilizza l'associazione dati bidirezionale per visualizzare il testo e per accettare l'inserimento del testo.
<!-- android:text must be placed before cursorPosition otherwise we'll get IndexOutOfBounds exception-->
<EditText
android:text="@={viewModel.noteText}"
cursorPosition="@{viewModel.cursorPosition}" />
Adattatore vincolante
@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}
Event
classe
La Event
classe qui è come un SingleLiveEvent scritto da Jose Alcérreca da Google. Lo uso qui per occuparmi della rotazione dello schermo. L'uso del singolo Event
farà in modo che il cursore non salti alla fine del testo quando l'utente sta modificando il testo da qualche parte nel mezzo e lo schermo ruota. Manterrà la stessa posizione quando lo schermo ruota.
Ecco la Event
lezione:
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
}
Questa è la soluzione che funziona per me e offre una buona esperienza utente. Spero che sia d'aiuto anche nei tuoi progetti.