ViewModel、LiveData、およびデータバインディングの場合
EditText
ノートアプリで複数行をサポートするためにこの機能が必要でした。ユーザーがメモテキストのあるフラグメントに移動したときに、テキストの最後にカーソルが必要でした。
djleopによって提案された解決策が近づきました。しかし、この問題は、ユーザーが編集のためにテキストの途中にカーソルを置いて入力を開始すると、カーソルが再びテキストの最後にジャンプすることです。これは、LiveData
は、が新しい値を出力し、カーソルが再びテキストの最後にジャンプして、ユーザーが途中でテキストを編集できないために発生しました。
これを解決するために、フラグを使用して1回だけMediatorLiveData
の長さを割り当てString
ます。これにより、LiveDataは値を1回だけ読み取ります。つまり、ユーザーがフラグメントに移動したときです。その後、ユーザーはカーソルをテキストを編集したい場所に置くことができます。
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があり、これは、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
ここのクラスは、GoogleのJoseAlcérrecaが作成したSingleLiveEventのようなものです。ここでは、画面の回転を処理するために使用します。シングル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
}
これは私にとって有効なソリューションであり、優れたユーザーエクスペリエンスを提供します。それがあなたのプロジェクトにも役立つことを願っています。