Для ViewModel, LiveData и привязки данных
Мне нужна была эта функциональность для EditText
поддержки многострочной печати в моем приложении заметок. Я хотел курсор в конце текста, когда пользователь переходит к фрагменту с текстом заметки.
Решение, предложенное djleop, близко. Но проблема в том, что, если пользователь помещает курсор где-то посередине текста для редактирования и начинает печатать, курсор снова переместится в конец текста. Это произошло потому, чтоLiveData
выдаст новое значение, а курсор снова перейдет к концу текста, в результате чего пользователь не сможет редактировать текст где-то посередине.
Чтобы решить это, я использую MediatorLiveData
и назначаю ему длину String
только один раз, используя флаг. Это приведет к тому, что LiveData прочитает значение только один раз, то есть когда пользователь перейдет к фрагменту. После этого пользователь может разместить курсор в любом месте, где он хочет редактировать текст.
ViewModel
private var accessedPosition: Boolean = false
val cursorPosition = MediatorLiveData<Event<Int>>().apply {
addSource(yourObject) { value ->
if(!accessedPosition) {
setValue(Event(yourObject.note.length))
accessedPosition = true
}
}
}
Вот yourObject
еще одна LiveData, извлеченная из базы данных, которая содержит текст String, который вы отображаете в EditText
.
Затем привяжите это MediatorLiveData
к вашему EditText, используя привязывающий адаптер.
XML
Использует двустороннюю привязку данных для отображения текста и принятия ввода текста.
<!-- android:text must be placed before cursorPosition otherwise we'll get IndexOutOfBounds exception-->
<EditText
android:text="@={viewModel.noteText}"
cursorPosition="@{viewModel.cursorPosition}" />
Связующий адаптер
@BindingAdapter("cursorPosition")
fun bindCursorPosition(editText: EditText, event: Event<Int>?) {
event?.getContentIfNotHandled()?.let { editText.setSelection(it) }
}
Event
учебный класс
Event
Класс здесь как SingleLiveEvent написанный Хосе Alcérreca от Google. Я использую это здесь, чтобы заботиться о повороте экрана. Использование сингла Event
гарантирует, что курсор не перейдет к концу текста, когда пользователь редактирует текст где-то посередине и экран поворачивается. Он будет сохранять ту же позицию, когда экран вращается.
Вот Event
класс:
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
}
Это решение, которое работает для меня и обеспечивает хороший пользовательский опыт. Надеюсь, это поможет и в ваших проектах.