Я исследую новый компонент архитектуры ViewModel, намереваясь перенести в него мое приложение, активность за деятельностью. Мое приложение уже использует привязку данных и имеет некоторые объекты модели в качестве BaseObservable. В настоящее время у меня есть одно действие, которое выполняет вызов API, и в случае сбоя вызова я возвращаю изменение в пользовательском интерфейсе. Я использую двустороннюю привязку для этого. Итак, теперь я создаю класс ViewModel для действия и перемещаю к нему некоторую бизнес-логику и вызов API. После этого последовательность возврата при неудачном вызове API больше не обновляет пользовательский интерфейс. Я вижу, что изменение значения достигает моей модели BaseObservable, которая вызывает метод notifyChanged, однако модель имеет mCallbacks == null
, поэтому нет никаких слушателей для обновления пользовательского интерфейса после изменений модели. Вот кусочки:
Активная часть 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
вернется обратно, но это не так.