Para ViewModel, LiveData y Enlace de datos
Necesitaba esta funcionalidad EditText
con soporte multilínea en mi aplicación de notas. Quería el cursor al final del texto cuando el usuario navega hacia el fragmento que tiene texto de nota.
La solución sugerida por el djleop se acerca. Pero el problema con esto es que, si el usuario coloca el cursor en algún lugar en el medio del texto para editarlo y comienza a escribir, el cursor volvería al final del texto. Esto sucedió porque elLiveData
emitiría el nuevo valor y el cursor saltaría al final del texto nuevamente, lo que provocaría que el usuario no pudiera editar el texto en algún lugar en el medio.
Para resolver esto, lo uso MediatorLiveData
y le asigno la longitud de String
solo una vez usando una bandera. Esto hará que LiveData lea el valor solo una vez, es decir, cuando el usuario navegue hasta el fragmento. Después de eso, el usuario puede colocar el cursor en cualquier lugar donde desee editar el texto allí.
ViewModel
private var accessedPosition: Boolean = false
val cursorPosition = MediatorLiveData<Event<Int>>().apply {
addSource(yourObject) { value ->
if(!accessedPosition) {
setValue(Event(yourObject.note.length))
accessedPosition = true
}
}
}
Aquí, yourObject
hay otro LiveData recuperado de la base de datos que contiene el texto de cadena que está mostrando en elEditText
.
Luego vincule esto MediatorLiveData
a su EditText usando un adaptador de encuadernación.
XML
Utiliza el enlace de datos bidireccional para mostrar texto y aceptar la 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 enlace
@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}
Event
clase
La Event
clase aquí es como un evento SingleLiveEvent escrito por José Alcérreca de Google. Lo uso aquí para encargarme de la rotación de la pantalla. El uso del sencillo Event
asegurará que el cursor no salte al final del texto cuando el usuario esté editando el texto en algún lugar en el medio y la pantalla gire. Mantendrá la misma posición cuando la pantalla gire.
Aquí está la Event
clase:
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 es la solución que funciona para mí y proporciona una buena experiencia de usuario. Espero que ayude en sus proyectos también.