สำหรับ ViewModel, LiveData และ Data binding
ฉันต้องการฟังก์ชั่นนี้สำหรับ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
}
นี่เป็นวิธีแก้ปัญหาที่เหมาะกับฉันและมอบประสบการณ์การใช้งานที่ดี หวังว่ามันจะช่วยในโครงการของคุณด้วย