I would like to create ChipGroup 2-way binding adapter. I have copied default RadioGroup binding adapter with some changes but it doesn't work for both ways. In case of setting data to observable programmatically, ChipGroup retrieve changes from it. But manually Chip selection doesn't set changes to observable.
Here is my adapter
@InverseBindingMethods(InverseBindingMethod(type = ChipGroup::class, attribute = "android:checkedButton", method = "getCheckedRadioButtonId"))
class ChipGroupBindingAdapter {
companion object {
@JvmStatic
@BindingAdapter("android:checkedButton")
fun setCheckedChip(view: ChipGroup?, id: Int) {
if (id != view?.checkedChipId) {
view?.check(id)
}
}
@JvmStatic
@BindingAdapter(value = ["android:onCheckedChanged", "android:checkedButtonAttrChanged"], requireAll = false)
fun setChipsListeners(view: ChipGroup?, listener: ChipGroup.OnCheckedChangeListener?,
attrChange: InverseBindingListener?) {
if (attrChange == null) {
view?.setOnCheckedChangeListener(listener)
} else {
view?.setOnCheckedChangeListener { group, checkedId ->
listener?.onCheckedChanged(group, checkedId)
attrChange.onChange()
}
}
}
}
}
Layout file:
<android.support.design.chip.ChipGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkedButton="@{viewModel.checkedBtnObs}"
app:singleSelection="true">
<android.support.design.chip.Chip
android:id="@+id/first_chip"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="110dp"
android:layout_height="wrap_content"
android:checkable="true"
android:text="@string/month_12"
app:chipBackgroundColor="@drawable/chip_background_selector" />
<android.support.design.chip.Chip
android:id="@+id/second_chip"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="110dp"
android:layout_height="wrap_content"
android:checkable="true"
android:text="@string/month_6"
android:textAlignment="center"
app:chipBackgroundColor="@drawable/chip_background_selector" />
<android.support.design.chip.Chip
android:id="@+id/third_chip"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="110dp"
android:layout_height="wrap_content"
android:checkable="true"
android:text="@string/month_1"
app:chipBackgroundColor="@drawable/chip_background_selector" />
</android.support.design.chip.ChipGroup>
And observable:
val checkedBtnObs = ObservableInt(R.id.second_chip)
Finally, I found solution.
InverseBindingMethod method should be getCheckedChipId
instead of getCheckedRadioButtonId
Also, @=
should be added to xml android:checkedButton="@{viewModel.checkedBtnObs}"
like this android:checkedButton="@={viewModel.checkedBtnObs}"
Now this adapter can be used for ChipGroup 2-way binding
@InverseBindingMethods(InverseBindingMethod(type = ChipGroup::class, attribute = "android:checkedButton", method = "getCheckedChipId"))
class ChipGroupBindingAdapter {
companion object {
@JvmStatic
@BindingAdapter("android:checkedButton")
fun setCheckedChip(view: ChipGroup?, id: Int) {
if (id != view?.checkedChipId) {
view?.check(id)
}
}
@JvmStatic
@BindingAdapter(value = ["android:onCheckedChanged", "android:checkedButtonAttrChanged"], requireAll = false)
fun setChipsListeners(view: ChipGroup?, listener: ChipGroup.OnCheckedChangeListener?,
attrChange: InverseBindingListener?) {
if (attrChange == null) {
view?.setOnCheckedChangeListener(listener)
} else {
view?.setOnCheckedChangeListener { group, checkedId ->
listener?.onCheckedChanged(group, checkedId)
attrChange.onChange()
}
}
}
}
}
I got ChipGroup
with DataBinding
working as expected and using the recommended way of InverseDataBinding
, here's my implementation if anyone wish to know:
SomeBindingAdapters.kt
:
...
object PropertyTypeFilterBindingAdapters {
@BindingAdapter("propertyTypeFilter")
@JvmStatic
fun ChipGroup.bindPropertyTypeFilter(marsApiFilter: MarsApiFilter?) =
marsApiFilter?.let { filter ->
when (filter) {
MarsApiFilter.ALL -> check(R.id.filter_all_properties_chip)
MarsApiFilter.RENT -> check(R.id.filter_properties_for_rent_chip)
MarsApiFilter.BUY -> check(R.id.filter_properties_for_buy_chip)
}
}
@InverseBindingAdapter(attribute = "propertyTypeFilter")
@JvmStatic
fun ChipGroup.convertToMarsApiFilter(): MarsApiFilter = when (checkedChipId) {
R.id.filter_properties_for_rent_chip -> MarsApiFilter.RENT
R.id.filter_properties_for_buy_chip -> MarsApiFilter.BUY
else -> MarsApiFilter.ALL
}
@BindingAdapter("propertyTypeFilterAttrChanged")
@JvmStatic
fun ChipGroup.setListeners(attrChange: InverseBindingListener?) =
setOnCheckedChangeListener { _, _ -> attrChange?.onChange() }
}
...
MarsApiFilter
is a enum class for my specific case.some_layout.xml
:
...
<com.google.android.material.chip.ChipGroup
android:id="@+id/filter_property_type_chip_group"
...
bind:propertyTypeFilter="@={viewModel.propertyTypeFilter}"
>
<com.google.android.material.chip.Chip
android:id="@+id/filter_all_properties_chip"
...
/>
<com.google.android.material.chip.Chip
android:id="@+id/filter_properties_for_rent_chip"
...
/>
<com.google.android.material.chip.Chip
android:id="@+id/filter_properties_for_buy_chip"
...
/>
</com.google.android.material.chip.ChipGroup>
...
bind
is just a fancy namespace declared as a regular one: xmlns:bind="http://schemas.android.com/apk/res-auto"
SomeViewModel.kt
:
...
val propertyTypeFilter = MutableLiveData<MarsApiFilter>().apply { value = MarsApiFilter.ALL }
...
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