Как обновить текст всех элементов EditTexts в RecyclerView с двусторонней привязкой данных

У меня есть RecyclerView, который содержит несколько CardView, и каждый CardView содержит EditText, который умножается пользователь указал количество, умноженное на конкретную ставку (скорость исходит от конечной точки, и скорость различается для каждой строки). Для CardViews я использую привязку данных.

Пример использования приложения:

Приложение должно показать, сколько, например, 2, 7,89, 14,34 или 110 евро в других валютах.

  1. Пользователь вводит суммуEditText) в любой строке, каждая строка имеет поле скорость (скорость исходит от конечной точки API) с полем другое значение
  2. Введенная пользователем сумма умножается на ставку.
  3. Каждую строку в RecyclerView следует обновить.

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

Это мой класс данных для привязки данных:

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)
        }

}

Это мой EditText в файле item_currency.xml

<EditText
                android:id="@+id/currency_rate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                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
    }

}

Здесь я получаю доступ к RecyclerView через расширения Kotlin для Android:

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

Вот RecyclerView из activity_main.xml

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        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" />

Вот адаптер для RecyclerView:

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)

person Fahri Can    schedule 11.07.2020    source источник
comment
Вы нашли какое-нибудь решение? Как обновить пользовательский интерфейс после ввода данных? У меня такая же проблема, не вызывается наблюдатель LiveData. Можем ли мы инициировать уведомление об изменении LiveData, не вводя новый изменяемый объект с помощью onTextChange? Пожалуйста, поделитесь своими выводами.   -  person Anshul Kabra    schedule 08.09.2020
comment
Нет, к сожалению, я не нашел подходящего решения этой проблемы   -  person Fahri Can    schedule 08.09.2020


Ответы (1)


Возможно, вы захотите создать еще один Observable в -say- вашей модели представления или адаптера и привязать его как переменную к элементам адаптера.

Вот так:

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, это может помочь вам быстрее разобраться в подобных вещах.

person diareuse    schedule 13.07.2020
comment
К сожалению не работает. Второй параметр DataBindingUtil.inflate ‹ViewDataBinding› () должен быть Int. Откуда я взял этот Инт? - person Fahri Can; 13.07.2020
comment
Если бы вы следовали инструкциям по линту, вы бы смогли решить эту проблему :) в любом случае, я сильно обманываю вас. посмотрите еще раз на строчку с накачкой переплета. Я добавил inflater в качестве первого параметра. - person diareuse; 13.07.2020
comment
Сбой приложения в строке: val binding = DataBindingUtil.inflate ‹ViewDataBinding› (inflater, viewType, parent, false) сообщение: Resources $ NotFoundException: Resource ID # 0x0 - person Fahri Can; 13.07.2020
comment
Поменять местами viewType в моем примере на R.layout.item_currency - person diareuse; 14.07.2020
comment
Хорошо, но как бы выглядел мой метод onBindViewHolder (держатель: CurrencyViewHolder, позиция: Int)? Когда мне нужно использовать Holder.itemView, это снова вызывает новые проблемы. - person Fahri Can; 14.07.2020