I have an RecyclerView which holds some CardViews and each CardView contains EditText which multiplies the user given amount times a specific rate (the rate comes from an endpoint and the rate is different per row). For the CardViews I am using data binding.
Use case of the app:
The app should show how much for example 2, 7.89, 14.34, or 110 € are in other currencies.
Now is the question how to update the text of all EditTexts elements in a RecyclerView with two-Way data binding
This is my data class for data binding:
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)
}
}
This is my EditText in the 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" />
This is my Application class which stores the user entered amount:
class CurrencyApplication : Application() {
companion object {
var userEnteredAmount: Double = 1.00
}
}
Here I access the RecyclerView through Kotlin Android Extensions:
recycler_view.apply {
setHasFixedSize(true)
itemAnimator = DefaultItemAnimator()
adapter = currencyAdapter
}
Here is the RecyclerView from 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" />
Here is the Adapter for the 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)
You might probably want to create another Observable
in -say- your viewModel or adapter and bind it as a variable to the adapter items.
Like so:
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)
}
}
If you encounter issues with other views eagerly updating the rate variable, try to making a custom data binding adapter to only allow views triggering the updates when they are in focus.
@BindingAdapter("android:textAttrChanged")
fun TextView.setOnTextAttrChangedInFocus(listener: InverseBindingListener) {
addTextChangedListener(afterTextChanged = {
if(isFocused) {
listener.onChange()
}
})
}
(example uses androidx.core extension)
I hope it helps ;)
Check out teanity it might help you figure out some stuff like this faster.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With