Невозможно получить доступ к свойству пользовательских LiveData с помощью привязки данных - PullRequest
0 голосов
/ 01 февраля 2019

Я пытаюсь использовать живые данные с привязкой данных для TextInputLayout, используя такой класс:

class MutableLiveDataWithErrorText<T> : MutableLiveData<T>() {
    val errorText = MutableLiveData<String>().apply { value = "" }
}

Теперь, когда пытаюсь использовать его для текста ошибки в xml,

<layout>
    <data>
        <!-- ... -->
        <variable
            name="target"
            type="com.my.app.MutableLiveDataWithErrorText&lt;String&gt;" />

    </data>

    <com.google.android.material.textfield.TextInputLayout
        app:errorEnabled="true"
        app:errorText="@{target.errorText}">

        <!-- ... -->

    </com.google.android.material.textfield.TextInputLayout>
</layout>

Я получаю эту ошибку:

Cannot find getter 'getErrorText' for type String.

Я попытался создать BindingAdapter, чтобы обойти это:

@BindingAdapter("errorTextLive")
fun setErrorTextLive(
    view: TextInputLayout,
    liveDataWithErrorText: MutableLiveDataWithErrorText<String>
) {
    if (liveDataWithErrorText.errorText.value.isNullOrEmpty().not()) {
        view.error = liveDataWithErrorText.errorText.value
    }
}

с назначением xml, измененным на:

app:errorTextLive="@{target}"

, что делает компиляцию успешной, но изменения target.errorText больше не наблюдаются, вместо этого он наблюдает изменения в target, обновляя errorText только при изменении значения target.

Есть ли способ заставить его соблюдать target.errorText?

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Это плохой шаблон для передачи модели представления в виде набора полей вместо одного составного объекта.Если вам нужно передать как минимум две переменные в привязку данных, вам нужно создать модель представления с этими полями - это поможет вам сделать изменения более гибкими.

Например, вы можете определить модель представления, как эта:

class SimpleViewModel : ViewModel() {

    /**
     * Expose MutableLiveData to enable two way data binding
     */
    val textData = MutableLiveData<String>().apply { value = "" }
    /**
     * Expose LiveData for read only fields
     */
    val errorText = Transformations.map(textData, ::validateInput)

    /**
     * Validate input on the fly
     */
    private fun validateInput(input: String): String? = when {
        input.isBlank() -> "Input is blank!"
        else -> null
    }
}

на стороне макета это очень близко к вашему варианту:

<layout>
    <data>
        <variable
            name="vm"
            type="com.example.SimpleViewModel" />

    </data>

    <android.support.design.widget.TextInputLayout
        app:errorEnabled="true"
        app:errorText="@{vm.errorText}">

        <android.support.design.widget.TextInputEditText
            android:text="@={vm.textData}"/>

    </android.support.design.widget.TextInputLayout>
</layout>

ПРИМЕЧАНИЕ: SimpleViewModel не нужно расширять ViewModel, но это позволяетваши данные, чтобы выжить изменения конфигурации из коробки

0 голосов
/ 02 февраля 2019

Есть ли способ заставить его соблюдать target.errorText?

Я так не думаю.Проблема в том, что библиотека привязки данных сначала разрешает target внутри target.errorText и видит, что она имеет тип MutableLiveData<String>, затем она автоматически получает значение target, имеющее тип String, и затем пытается вызватьgetErrorText () для этого объекта String, что приводит к ошибке, которую вы видите.

У меня был похожий вариант использования, и я прибег к созданию следующего класса:

class <T> ValidatableValue {

    val liveData = MutableLiveData<T>() // The actual value.

    // Other helper livedatas and functions.
    val isValid = MutableLiveData<Boolean>()
    val errorMessage = MutableLiveData<String>()

    fun validate() { ... }
}

Тогда я могуиспользуйте все эти объекты LiveData в макете привязки данных.

...