我正在研究新的体系结构ViewModel组件,打算逐个活动地将我的应用迁移到该组件。我的应用程序已使用数据绑定,并具有一些模型对象作为BaseObservable。当前,我有一个活动进行API调用,如果调用失败,我将还原UI中的更改。我为此使用双向绑定。现在,我为该活动创建一个ViewModel类,并将一些业务逻辑和API调用移至该活动。完成此操作后,失败的API调用上的还原序列将不再更新UI。我看到值更改到达了我的BaseObservable模型,该模型调用notifyChanged方法,但是该模型具有mCallbacks == null
,因此在模型更改后没有任何侦听器来更新UI。这是碎片:
OnCreate的活动部分
this.dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_settings)
val viewModel = AppSettingsViewModel(this.application)
this.dataBinding.viewModel = viewModel
this.dataBinding.setLifecycleOwner(this)
this.lifecycle.addObserver(this.dataBinding.viewModel!!)
this.dataBinding.context = this
部分布局:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="context" type="letstwinkle.com.twinkle.AppSettingsActivity" />
<variable name="viewModel" type="letstwinkle.com.twinkle.viewmodel.AppSettingsViewModel"/>
...imports...
</data>
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer"
android:layout_width="match_parent" android:layout_height="match_parent"
>
...more nested views...
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginStart="12dp"
android:text="@string/new_matches"
android:checked="@={viewModel.settings.pnMatches}"
android:textColor="@color/settingsItem"
android:onClick="@{(v) -> viewModel.toggleNewMatches(((Checkable)v).isChecked)}"
android:enabled="@{viewModel.settings != null}"
/>
</layout>
最后是ViewModel / SettingsModel
internal class AppSettingsViewModel(app: Application) : AndroidViewModel(app), VolleyErrorObservable,
ResponseHandler<SubmitResult>, LifecycleObserver
{
var settings: SettingsModel? = null
val account = User.account
val demoMessageText = MutableLiveData<String>()
override val volleyError = MutableLiveData<VolleyError>()
fun toggleNewMatches(newValue: Boolean) {
this.twinkleApplication.trackEvent("click:togglesetting", "App Settings Toggle Setting",
Bundle().apply { putString("setting", "pn_match") })
updateBoolSetting("pn_match", newValue) { set, b -> set.pnMatches = b}
}
private fun updateBoolSetting(setting: String, newValue: Boolean, reverter: (SettingsModel, Boolean) -> Unit) {
val obj = this.settings!!.jsonForSetting(setting, newValue)
val suh = SettingsUpdateHandler(this.settings!!, setting == "mp_enabled")
{ settings -> reverter(settings, !newValue) }
APIClient.updateSettings(obj, suh)
}
...
}
private class SettingsUpdateHandler(val settings: SettingsModel,
val isMPEnabled: Boolean = false,
val revertFun: (SettingsModel) -> Unit)
: ResponseHandler<SubmitResult>
{
override fun onErrorResponse(error: VolleyError) {
revertFun(settings)
if (weakToast?.get()?.view?.isShown == true) {
weakToast?.get()?.cancel()
}
val toast = Toast.makeText(TwinkleApplication.instance,
R.string.failed_save_settings, Toast.LENGTH_LONG)
...
}
override fun onResponse(response: SubmitResult) {
...
}
}
@Table(database = Database::class, useBooleanGetterSetters = false)
internal class SettingsModel() : BaseObservable(), Model {
@NotNull @Column @get:Bindable var pnMatches: Boolean = false
set(value) {
Log.d("SettingsModel", "set pnMatches: value=$value, field=$field")
val changed = field != value
field = value
notifyIfChanged(BR.pnMatches, changed)
}
...
private inline fun notifyIfChanged(field: Int, changed: Boolean) {
if (changed)
notifyPropertyChanged(field)
}
}
我希望在将viewModel.settings.pnMatches
的值改回时将Switch切换回去,但是不会。
答案 0 :(得分:0)
我在数据绑定文档页面之一上找到了答案,其中包括关于ViewModel / LiveData用法的讨论:ViewModel需要实现Observable本身,并在不是LiveData的Observable字段上使用Bindable批注:
internal class AppSettingsViewModel(app: Application) : AndroidViewModel(app), VolleyErrorObservable,
ResponseHandler<SubmitResult>, NotifiableObservable by BaseNotifiableObservable()
{
@get:Bindable
var settings: SettingsModel? = null
.....
NotifiableObservable
位是Kotlin惯用语,用于通过委托实现Observable
,这是我在网上找到的。