W przypadku ViewModel, LiveData i powiązania danych
Potrzebowałem tej funkcji EditText
z obsługą wielu linii w mojej aplikacji do notatek. Chciałem kursor na końcu tekstu, gdy użytkownik przechodzi do fragmentu zawierającego tekst notatki.
Rozwiązanie sugerowane przez djleopa jest bliskie. Problem polega jednak na tym, że jeśli użytkownik umieści kursor gdzieś pośrodku tekstu do edycji i zacznie pisać, kursor przeskoczy ponownie na koniec tekstu. Stało się tak, ponieważ LiveData
wyemitowałyby nową wartość, a kursor ponownie przeskoczyłby na koniec tekstu, powodując, że użytkownik nie byłby w stanie edytować tekstu gdzieś pośrodku.
Aby rozwiązać ten problem, używam MediatorLiveData
i przypisuję mu długość String
tylko raz za pomocą flagi. Spowoduje to, że LiveData odczyta wartość tylko raz, to znaczy, gdy użytkownik przejdzie do fragmentu. Następnie użytkownik może umieścić kursor w dowolnym miejscu, w którym chce edytować tekst.
ViewModel
private var accessedPosition: Boolean = false
val cursorPosition = MediatorLiveData<Event<Int>>().apply {
addSource(yourObject) { value ->
if(!accessedPosition) {
setValue(Event(yourObject.note.length))
accessedPosition = true
}
}
}
Oto yourObject
kolejna funkcja LiveData pobrana z bazy danych zawierającej tekst String, który wyświetlasz w EditText
.
Następnie powiąż to MediatorLiveData
z tekstem edycji za pomocą adaptera wiązania.
XML
Używa dwukierunkowego powiązania danych do wyświetlania tekstu oraz akceptowania wprowadzania tekstu.
<!-- android:text must be placed before cursorPosition otherwise we'll get IndexOutOfBounds exception-->
<EditText
android:text="@={viewModel.noteText}"
cursorPosition="@{viewModel.cursorPosition}" />
Adapter do wiązania
@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}
Event
klasa
Ta Event
klasa jest jak SingleLiveEvent napisany przez Jose Alcérreca z Google. Używam go tutaj, aby zadbać o rotację ekranu. Użycie singla Event
sprawi, że kursor nie przeskoczy na koniec tekstu, gdy użytkownik edytuje tekst gdzieś pośrodku, a ekran się obraca. Utrzymuje tę samą pozycję, gdy ekran się obraca.
Oto Event
klasa:
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
}
To rozwiązanie działa dla mnie i zapewnia dobre wrażenia użytkownika. Mam nadzieję, że pomoże to również w twoich projektach.