体系结构ViewModel中的BaseObservable字段未添加任何观察者回调

时间:2019-05-23 01:43:13

标签: android android-databinding android-architecture-components android-mvvm

我正在研究新的体系结构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切换回去,但是不会。

1 个答案:

答案 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,这是我在网上找到的。