Android Двусторонняя привязка данных с двойным (Kotlin) - PullRequest
1 голос
/ 06 августа 2020

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

class StockLoadTaskModel : ViewModel() {
    ....
    ....
 
    var d: Double = 10.0
}

Это связано со следующим макетом:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    
    <data>
        <import type="android.view.View" />
        <import type="it.kfi.lorikeetmobile.extras.Converter" alias="Converter"/
        <variable
            name="viewModel"
            type="it.kfi.lorikeetmobile.stock.models.StockLoadTaskModel" />
        <variable
            name="view"
            type="it.kfi.lorikeetmobile.stock.ui.movements.StockLoadTaskFragment
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        ...
    
        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="4dp"
            android:layout_marginEnd="8dp">
    
            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/et_code"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/hint_et_item_code"
                android:text="@={viewModel.itemCode}" />
        </com.google.android.material.textfield.TextInputLayout>
    
        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="4dp"
            android:layout_marginEnd="8dp">
    
            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/et_quantity"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="numberDecimal"
                android:text="@={Converter.doubleToString(d)}"
                android:hint="@string/quantity" />
        </com.google.android.material.textfield.TextInputLayout>
    
        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="4dp"
            android:layout_marginEnd="8dp">
    
            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/et_note"
                android:lines="3"
                android:scrollbars="vertical"
                android:overScrollMode="ifContentScrolls"
                android:gravity="top"
                android:inputType="textMultiLine"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/hint_et_note"
                android:text="@={viewModel.selectedItem.detail.note}"/>
        </com.google.android.material.textfield.TextInputLayout>
        
        ...
    </LinearLayout>

И у меня также есть следующий объект Converter :

object Converter {
    
    @JvmStatic
    @InverseMethod("stringToDouble")
    fun doubleToString(value: Double?): String? {
    
        if (value == null) {
            return null
        }
        return DecimalFormat(ClientConfiguration.currentConfig.decimalFormat).format(value)
    }
    
    @JvmStatic
    fun stringToDouble(value: String?): Double? {
    
        if (value == null) {
            return null
        }
        val v = DecimalFormat(ClientConfiguration.currentConfig.decimalFormat).parse(value)
        return v.toDouble()
    }
}

Если я установил: android:text="@={Converter.doubleToString(d)}" (двусторонняя привязка данных), в EditText с идентификатором et_quantity я получаю следующую ошибку:

...error: cannot find symbol

Если Я меняю его на одностороннюю привязку данных, например: android:text="@{Converter.doubleToString(d)}", работает. Похоже, диспетчер привязки не может распознать обратный метод.

Кто-нибудь может мне помочь? Спасибо.

1 Ответ

1 голос
/ 06 августа 2020

Почему происходит ошибка?

Когда вы определяете двустороннюю привязку данных, как это android:text="@={Converter.doubleToString(d)}", возникает вопрос: где данные должны быть переданы из EditText? Должен ли он быть передан в Converter.doubleToString или, может быть, другую stati c функцию из Converter, возможно, в результат Converter.doubleToString(d) или в d переменную?

Вы должны быть точными.

Вы ожидаете, что это d, компилятор ожидает, что это результат Converter.doubleToString(d). На самом деле, ни то, ни другое не будет работать.

Теперь EditText работает с символами. Он ничего не знает о double, int, float, byte, short, boolean или о чем-либо еще, кроме строки.

Это означает, что для реализации двусторонней привязки данных ваш источник:

  • обязательно возвращаемое значение типа String;
  • должно быть присваиваемым.

Как решить проблему?

Android компоненты архитектуры знакомят нас с классом ObservableField. Есть готовые к использованию ObservableBoolean, ObservableChar, ObservableFloat и еще несколько. Если вы откроете ссылку из предыдущего предложения, вы увидите все классы Observable... на левой панели.

Нет ObservableString, но ObservableField принимает общий c тип. Таким образом, вы можете определить переменную, которая является частью привязки данных, как ObservableField<String>("defaultValueHere").

Итак, у вас должно быть:

class StockLoadTaskModel : ViewModel() {
    ....
    ....
    var d: Double = 10.0
    var dataBindingVariable = ObservableField<String>(d.toString())
}

dataBindingVariable всегда будет возвращать вам содержимое EditText, к которому вы его привязали. Вы можете получить это значение и безопасно преобразовать его в double.

class StockLoadTaskModel : ViewModel() {
    ....
    ....
    var d: Double = 10.0
    var dataBindingVariable = 
        object: ObservableField<String>(d.toString()) {
            override fun set(value: String?) {
                super.set(value)
                // a value has been set
                d = value.toDoubleOrNull() ?: d
            }
        }
}

Объявление макета будет выглядеть так, как для поля ввода:

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="4dp"
            android:layout_marginEnd="8dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/et_quantity"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="numberDecimal"
                android:text="@={viewModel.dataBindingVariable}"
                android:hint="@string/quantity" />
        </com.google.android.material.textfield.TextInputLayout>

И не будет необходимости в object Converter.

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

...