Android 双向数据绑定 (Kotlin)

Android Two-Way Data Binding with Double (Kotlin)

我有一个ViewModelclass定义如下:

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)}"(双向数据绑定),在 ID 为 et_quantityEditText 中,我得到以下错误:

...error: cannot find symbol

如果我将其更改为单向数据绑定,例如:android:text="@{Converter.doubleToString(d)}",它就可以工作。绑定管理器似乎无法识别逆向方法。

有人可以帮助我吗?谢谢。

为什么会出现错误?

当您像示例中那样定义 two-way 数据绑定时 android:text="@={Converter.doubleToString(d)}" 问题是:什么 function/object 将接收您从 传回的数据 EditText 当用户输入数据时?数据应该传递给 Converter.doubleToString 还是 Converter 的其他一些静态函数?也许是 Converter.doubleToString(d) 的结果或 d 变量?

一定要准确

你期望它是 d,编译器期望它是 Converter.doubleToString(d) 的结果。实际上,两者都行不通。

另一个问题是 EditText 确实对字符进行操作。它对 double、int、float、byte、short、boolean 或任何其他非字符串的东西一无所知。

这意味着为了实现two-way数据绑定你的来源:

  • 必须 return 类型为 String 的值
  • 必须是可分配的。

如何解决这个问题?

Android架构组件向我们介绍ObservableFieldclass。可以使用 ObservableBooleanObservableCharObservableFloat 和其他几个。 如果您打开上一句中的 link,您应该会在左侧窗格中看到所有 classes Observable...

没有 ObservableStringObservableField 接受通用类型。因此,您可以将作为数据绑定一部分的变量定义为 ObservableField<String>("defaultValueHere").

那么你应该拥有的是:

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

dataBindingVariable 将始终 return 您绑定到的 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

还有另一种实现数据绑定的方法two-way 我在这里不谈,因为已经有人回答了。 .