如何使用双向数据绑定更新 RecyclerView 中所有 EditTexts 元素的文本

Posted

技术标签:

【中文标题】如何使用双向数据绑定更新 RecyclerView 中所有 EditTexts 元素的文本【英文标题】:How to update the text of all EditTexts elements in a RecyclerView with two-way data binding 【发布时间】:2020-11-01 11:01:18 【问题描述】:

我有一个 RecyclerView,其中包含一些 CardViews,每个 CardView 都包含 EditText 相乘 用户给定的 amount 乘以特定的 rate(速率来自端点,每行的速率不同)。对于 CardViews,我正在使用数据绑定。

应用的用例:

应用应显示其他货币的金额,例如 2、7.89、14.34 或 110 欧元。

    用户在任何一行输入一个金额(在EditText中),每一行都有一个“rate”(rate来自一个API端点)字段具有不同的值 用户输入的 amount 乘以“rateRecyclerView 中的每一行都应该更新

现在的问题是如何使用双向数据绑定更新 RecyclerView 中所有 EditTexts 元素的文本

这是我用于数据绑定的数据类:

data class CurrencyItem(
    var flag: String,
    var shortName: String,
    var fullName: String,
    var rate: Double
) : BaseObservable() 

    @Bindable
    var rateTimesAmount: String = (CurrencyApplication.userEnteredAmount * rate).toString()
        set(amount) 
            val amountAsDouble = amount.toDouble()
            val number2digits: Double = String.format("%.2f", amountAsDouble).toDouble()
            CurrencyApplication.userEnteredAmount = number2digits
            field = number2digits.toString()
            notifyPropertyChanged(BR.rateTimesAmount)
        


这是我在 item_currency.xml 中的 EditText

<EditText
                android:id="@+id/currency_rate"
                android:layout_
                android:layout_
                android:layout_marginEnd="16dp"
                android:imeOptions="actionDone"
                android:inputType="numberDecimal"
                android:lines="1"
                android:maxLength="8"
                android:text="@=currency.rateTimesAmount"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="1183.068" />

这是我的 Application 类,用于存储用户输入的金额:

class CurrencyApplication : Application() 

    companion object 
        var userEnteredAmount: Double = 1.00
    


这里我通过 Kotlin Android Extensions 访问 RecyclerView

recycler_view.apply 
            setHasFixedSize(true)
            itemAnimator = DefaultItemAnimator()
            adapter = currencyAdapter
        

这是来自activity_main.xml的RecyclerView

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_
        android:layout_
        android:layout_margin="8dp"
        android:clipToPadding="false"
        android:paddingBottom="8dp"
        android:scrollbars="vertical"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

这是 RecyclerViewAdapter

class CurrencyAdapter(
    val currencies: ArrayList<CurrencyItem>
) : RecyclerView.Adapter<CurrencyViewHolder>() 
    
    override fun getItemCount() = currencies.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CurrencyViewHolder 
        val itemCurrencyBinding: ItemCurrencyBinding = DataBindingUtil.inflate(
            LayoutInflater.from(parent.context),
            R.layout.item_currency,
            parent,
            false
        )
        return CurrencyViewHolder(itemCurrencyBinding)
    

    override fun onBindViewHolder(holder: CurrencyViewHolder, position: Int) 
        holder.itemCurrencyBinding.currency = currencies[position]
    

    fun setUpCurrencies(newCurrencies: List<CurrencyItem>) 
        currencies.clear()
        currencies.addAll(newCurrencies)
        notifyDataSetChanged()
    


class CurrencyViewHolder(val itemCurrencyBinding: ItemCurrencyBinding) :
    RecyclerView.ViewHolder(itemCurrencyBinding.root)

【问题讨论】:

您找到解决方案了吗?输入后如何更新 UI?我有同样的问题,没有调用 LiveData 观察者。我们可以在不使用 onTextChange 引入新的 mutable 的情况下触发 LiveData 更改的通知吗?请分享您的发现。 不,很遗憾,我没有找到任何适合该问题的解决方案 【参考方案1】:

您可能希望在您的 viewModel 或适配器中创建另一个 Observable,并将其作为变量绑定到适配器项。

像这样:

class CurrencyAdapter: RecyclerView.Adapter<...>() 

    val rate = ObservableField(0.0)

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): RecyclerView.ViewHolder 
        val inflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, viewType, parent, false)
        binding.setVariable(BR.rate, rate)
        return YourViewHolder(binding.root)
    


如果您遇到其他视图急切更新速率变量的问题,请尝试制作自定义数据绑定适配器,以仅允许视图在焦点处于焦点时触发更新。

@BindingAdapter("android:textAttrChanged")
fun TextView.setOnTextAttrChangedInFocus(listener: InverseBindingListener) 
    addTextChangedListener(afterTextChanged = 
        if(isFocused) 
            listener.onChange()
        
    )

(示例使用 androidx.core 扩展)

希望对你有帮助;)


查看teanity,它可能会帮助您更快地找出类似的东西。

【讨论】:

不幸的是不起作用。 DataBindingUtil.inflate() 的第二个参数需要是一个 Int。我从哪里得到这个 Int? 如果您按照 lint 说明进行操作,您将能够解决该问题 :) 无论哪种方式,我都会误导您。再看一下绑定膨胀的线。我添加了 inflater 作为第一个参数。 应用崩溃上线:val binding = DataBindingUtil.inflate(inflater, viewType, parent, false) 消息为 Resources$NotFoundException: Resource ID #0x0 交换viewType,在我的例子中,与R.layout.item_currency 好的,但是我的方法 onBindViewHolder(holder: CurrencyViewHolder, position: Int) 会是什么样子?当我必须使用 holder.itemView 时,这又会导致新问题

以上是关于如何使用双向数据绑定更新 RecyclerView 中所有 EditTexts 元素的文本的主要内容,如果未能解决你的问题,请参考以下文章

VUE表单元素双向数据绑定

vue双向绑定(数据劫持+发布者-订阅者模式)

vue的数据双向绑定是怎么实现的

vue3绑定proxy数据不更新

浅谈Vue的双向绑定

vue2.x双向数据绑定原理