Использование LiveData с привязкой данных

При стабилизации компонентов Android Architecture я начал обновлять все основные базовые ViewModel до новой реализации ViewModel. По моему мнению, использование LiveData рекомендуется удерживать класс Model, поскольку он лучше справляется с жизненным циклом.

Мне нравится использовать Data Binding, потому что он делает код более четким в Java/Kotlin, и нет необходимости "смотреть" изменения значений для обновления пользовательского интерфейса. Однако макет с использованием Data Binding отслеживает только изменения данных, если Model (или ViewModel) расширяет BaseObservable и LiveData не делает, Я понимаю, что одна из основных целей LiveData должна соблюдаться и обновлять пользовательский интерфейс программно, но для простых обновлений Data Binding очень полезен.

Эта проблема уже сообщалась (GitHub и Переполнение стека) и сначала было сказано, что версия 1.0 будет иметь его, и теперь говорят, что эта функция находится в разработке.

Чтобы использовать как LiveData, так и Data Binding, я создал очень простую реализацию класса, которая расширяет BaseObservable:

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import android.databinding.BaseObservable

class ObservableMutableLiveData<T>() : BaseObservable() {

    private var data: MutableLiveData<T> = MutableLiveData()

    constructor(data: T) : this() {
        this.data.value = data
    }

    public fun set(value: T) {
        if (value != data.value) {
            data.value = value
            notifyChange()
        }
    }

    public fun get(): T? {
        return data.value
    }

    public fun getObservable(): LiveData<T> {
        return data
    }
}

Итак, в основном мой ObservableMutableLiveData является копией ObservableField с помощью LiveData для хранения модели и с этой реализацией макет обновления после каждого обновления модели.

Вопросы:

  • Это плохая реализация LiveData? Разве эта оболочка "ломает" функциональные возможности LiveData, например, с учетом жизненного цикла?
  • В моем понимании LiveData - это новый ObservableField. Правильно ли это?

Ответ 1

Android Studio 3.1 (в настоящее время в Canary 6) устранит эту проблему, поскольку LiveData может использоваться как observable field:

Обновления привязки данных:

Теперь вы можете использовать объект LiveData в качестве наблюдаемого поля в выражениях привязки данных. Класс ViewDataBinding теперь включает новый метод setLifecycle, который вам нужно использовать для наблюдения за объектами LiveData.

Источник: Android Studio 3.1 Canary 6 теперь доступен

Ответ 2

Для тех, кто сталкивался с этим вопросом, ищет пример, как я, вот один:

В макете .xml поместите элемент LiveData с его типом:

<layout>
    <data>
        <variable
            name="myString"
            type="android.arch.lifecycle.MutableLiveData&lt;String&gt;"/>
    </data>

    ...

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{myString}'
        ...
     />

    ...

</layout>

В вашем коде установите значение и владельца жизненного цикла:

MutableLiveData<String> myString = new MutableLiveData<>();

...

binding.setLifecycleOwner(this);
binding.setMyString(myString);

Вот оно :)

Обратите внимание, что значением по умолчанию для элементов LiveData является null, поэтому присвойте начальные значения, чтобы сразу получить желаемый эффект, или используйте этот для принудительной установки обнуляемости.

EDIT: Используйте androidx.lifecycle.MutableLiveData&lt;String&gt; как тип, если вы используете androidx.

Ответ 3

Для androidx будет:

androidx.lifecycle.MutableLiveData

<layout>
    <data>
        <variable
            name="myString"
            type="androidx.lifecycle.MutableLiveData&lt;String&gt;"/>
    </data>

    ...

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{myString}'
        ...
     />

    ...

</layout>

А для Котлина:

  val myStr = MutableLiveData<String>()

...

 binding.apply {
            setLifecycleOwner(this)
            this.myString = myStr
        }

Удачи! :)