Para ViewModel, LiveData e ligação de dados
Eu precisava dessa funcionalidade para EditText
suporte multilinhas no meu aplicativo de anotações. Eu queria o cursor no final do texto quando o usuário navega para o fragmento que possui o texto da nota.
A solução sugerida pelo djleop chega perto. Mas o problema é que, se o usuário colocar o cursor em algum lugar no meio do texto para edição e começar a digitar, o cursor saltará para o final do texto novamente. Isso aconteceu porque oLiveData
comando emitia o novo valor e o cursor pularia novamente para o final do texto, resultando no usuário não capaz de editar o texto em algum lugar no meio.
Para resolver isso, eu uso MediatorLiveData
e atribuo o comprimento String
apenas uma vez usando um sinalizador. Isso fará com que o LiveData leia o valor apenas uma vez, ou seja, quando o usuário navega para o fragmento. Depois disso, o usuário pode colocar o cursor em qualquer lugar que desejar editar o texto lá.
ViewModel
private var accessedPosition: Boolean = false
val cursorPosition = MediatorLiveData<Event<Int>>().apply {
addSource(yourObject) { value ->
if(!accessedPosition) {
setValue(Event(yourObject.note.length))
accessedPosition = true
}
}
}
Aqui yourObject
está outro LiveData recuperado do banco de dados que contém o texto String que você está exibindo no EditText
.
Em seguida, vincule isso MediatorLiveData
ao seu EditText usando o adaptador de ligação.
XML
Utiliza ligação de dados bidirecional para exibir texto e também para aceitar a entrada de texto.
<!-- android:text must be placed before cursorPosition otherwise we'll get IndexOutOfBounds exception-->
<EditText
android:text="@={viewModel.noteText}"
cursorPosition="@{viewModel.cursorPosition}" />
Adaptador de ligação
@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}
Event
classe
A Event
classe aqui é como um SingleLiveEvent escrito por Jose Alcérreca do Google. Eu o uso aqui para cuidar da rotação da tela. O uso do single Event
garantirá que o cursor não vá para o final do texto quando o usuário estiver editando o texto em algum lugar no meio e a tela girar. Ele manterá a mesma posição quando a tela girar.
Aqui está a Event
turma:
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
}
Esta é a solução que funciona para mim e fornece uma boa experiência do usuário. Espero que ajude em seus projetos também.