I am trying to implement two MaterialCardViews that should act like a Radiogroup. So if I click one, the other should be unchecked. I am using viewModel, liveData and custom two-way data binding to save these values for later purpose (sending per email).
I had success writing the .xml and implementing the check logic, but I struggle implementing uncheck logic.
<layout
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.example.app.data.viewmodel.EmailViewModel" />
</data>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardViewOne"
android:checkable="true"
android:clickable="true"
android:focusable="true"
<!-- Custom Two way databinding -->
app:state_checked="@={vm.cardOptionOneChecked}"
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardViewTwo"
android:checkable="true"
android:clickable="true"
android:focusable="true"
<!-- Custom Two way databinding -->
app:state_checked="@={vm.cardOptionTwoChecked}">
</com.google.android.material.card.MaterialCardView>
</layout>
class EmailViewModel @ViewModelInject constructor(
@Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// Variable for Id = cardViewOne
val cardOptionOneChecked = MutableLiveData<Boolean>()
// Variable for Id = cardViewTwo
val cardOptionTwoChecked = MutableLiveData<Boolean>()
}
@BindingAdapter("state_checked")
fun setStateChecked(view: MaterialCardView, liveData: MutableLiveData<Boolean>) {
if (view.isChecked != liveData.value) {
liveData.value = view.isChecked
}
}
@InverseBindingAdapter(attribute = "state_checked")
fun getStateChecked(view: MaterialCardView,): Boolean {
return view.isChecked
}
// I don't know what logic belongs here to make it work!
// Current approach just checks the current view and does nothing more. How can I save the last
// checked value?
@BindingAdapter("state_checkedAttrChanged")
fun setCheckedAttrListener(
view: MaterialCardView,
attrChange: InverseBindingListener,
) {
view.apply {
setOnClickListener { view.isChecked = true }
setOnCheckedChangeListener { card, isChecked ->
if (card.isChecked && card != view) {
card.isChecked = false
}
}
attrChange.onChange()
}
}
I appreciate every help, thank you very much!
P.S: If there is a better and easier way to achieve this e.g. telling the viewModel from the view to save isChecked, please inform me. MaterialCardView has implemented "isChecked" by default but no logic.
Okay, I've solved the Problem:
I actually don't saw any way to use two-way data binding to achieve the above written case. Here is the new Binding Adapter
// View = Clicked MaterialCard, liveData = value in viewModel
@BindingAdapter("state_checked")
fun setStateChecked(view: MaterialCardView, liveData: MutableLiveData<Boolean>) {
if (view.isChecked != liveData.value) {
if (liveData.value != null) {
view.isChecked = liveData.value!!
}
}
}
<layout
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.example.app.data.viewmodel.EmailViewModel" />
</data>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardViewOne"
android:checkable="true"
android:clickable="true"
android:focusable="true"
<!-- Deleted "=" -->
app:state_checked="@{vm.cardOptionOneChecked}"
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardViewTwo"
android:checkable="true"
android:clickable="true"
android:focusable="true"
<!-- Deleted "=" -->
app:state_checked="@{vm.cardOptionTwoChecked}">
</com.google.android.material.card.MaterialCardView>
</layout>
class EmailViewModel @ViewModelInject constructor(
@ApplicationContext context: Context,
@Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val cardOptionOneChecked = MutableLiveData<Boolean>()
val cardOptionTwoChecked = MutableLiveData<Boolean>()
// Added
fun firstCardClicked() {
cardOneChecked.value = true
cardTwoChecked.value = false
}
fun secondCardClicked() {
cardOneChecked.value = false
cardTwoChecked.value = true
}
}
cardViewOne.setOnClickListener {
viewModel.firstCardClicked()
}
cardViewTwo.setOnClickListener {
viewModel.secondCardClicked()
}
If someone has any questions, just write it in the comments, I will help.