如何解决数据绑定+ LiveData不适用于复杂的嵌套对象
我在LiveData和数据绑定库中遇到了一些意外行为。 我已经在此答案Producing HTML Tutorial中实现了CustomLiveData,因此我只能在父类中调用notifyChange()来更新UI。
我有父对象(为简便起见,一些方法被省略了):
class Day(val tasks: MutableList<RunningTask>,state: DayState = DayState.WAITING,var dayStartTime: Long = 0L,currentTaskPos: Int = 0): BaseObservable() {
var state: DayState = state
set(value) {
field = value
notifyChange()
}
var currentTaskPos: Int = currentTaskPos
set(value) {
field = value
notifyChange()
}
fun start() {
dayStartTime = System.currentTimeMillis()
state = DayState.ACTIVE
resetTasks()
tasks[currentTaskPos].start()
notifyChange()
}
}
子对象:
class RunningTask(
startTime: Long,var name: String = "",private val originalDuration: Long = 0L,val sound: String
): BaseObservable() {
var startTime: Long = startTime
set(value) {
field = value
uiStartTime = convertMillisToStringFormat(value)
}
@Bindable
var uiStartTime: String = convertMillisToStringFormat(startTime)
set(value) {
field = value
notifyPropertyChanged(BR.uiStartTime)
}
var duration: Long = originalDuration
set(value) {
field = value
}
var state: State = State.WAITING
var progress: Long = 0L
set(value) {
field = value
}
var timePaused: Long = 0L
var timeRemain: String = convertMillisToStringFormat(duration)
enum class State {
WAITING,ACTIVE,COMPLETED,DISABLED
}
fun start() {
state = State.ACTIVE
}
}
问题是,当我在Day的 tasks 字段中更改项目时,例如item.main_screen_task.xml的数据绑定没有更新。调用方法 start(),但是其他字段(例如 state )确实可以正确更新,所以我想问题出在里面的列表了。
fragment_main_screen.xml,在Day类字段任务中填充了recyclerview:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View"/>
<variable
name="viewmodel"
type="com.sillyapps.meantime.ui.mainscreen.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tasks"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="@{viewmodel.noTemplate ? View.GONE : View.VISIBLE}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/play_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/constraintLayout"
tools:listitem="@layout/item_main_screen_task"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
item_main_screen_task, taskState 属性基本上只是BindingAdapter,它根据Day的 state 枚举设置背景可绘制:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="task"
type="com.sillyapps.meantime.data.RunningTask" />
<variable
name="taskAdapterPosition"
type="Integer" />
<variable
name="clickListener"
type="com.sillyapps.meantime.ui.ItemClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:taskState="@{task.state}"
android:onClick="@{() -> clickListener.onClickItem(taskAdapterPosition)}">
<TextView
android:id="@+id/time"
style="@style/TimeItemStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@{task.uiStartTime}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/enter_name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="17:00" />
<TextView
android:id="@+id/enter_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="@{task.name}"
app:layout_constraintBottom_toBottomOf="@+id/time"
app:layout_constraintEnd_toStartOf="@+id/progress"
app:layout_constraintStart_toEndOf="@+id/time"
app:layout_constraintTop_toTopOf="@+id/time"
tools:text="Свободное время" />
<TextView
android:id="@+id/progress"
style="@style/TimeItemStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@{task.timeRemain}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/enter_name"
app:layout_constraintTop_toTopOf="@+id/time"
tools:text="01:00" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
谢谢。
解决方法
事实证明,该解决方案非常简单,但有些出乎意料
子类应该扩展BaseObservable,并在每个数据绑定字段的设置器上调用notifyChange(),诸如此类:
class RunningTask(
startTime: Long,var name: String = "",private val originalDuration: Long = 0L,val sound: String
): BaseObservable() {
var startTime: Long = startTime
set(value) {
field = value
uiStartTime = convertMillisToStringFormat(value)
}
@Bindable
var uiStartTime: String = convertMillisToStringFormat(startTime)
set(value) {
field = value
notifyPropertyChanged(BR.uiStartTime)
}
var state: State = State.WAITING
set(value) {
field = value
notifyChange()
}
...
}
在提出问题之前,我似乎已经在uiStartTime中实现了该功能,但是我只是不知道它起作用的确切原因
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。