Search code examples
androidradio-buttonradio-group

radio button in radio button group doesn't unselect when selecting other radio buttons


I have the following xml that uses a radio button group to add radio buttons to it dynamically.

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/list_item_sort_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <RadioGroup
        android:id="@+id/rbSortOptionGroup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Once the layout is inflated it looks something like this: Radio buttons

This is in a Dialog fragment and the user will select a radio button and click apply will close the dialog.

When the user opens the dialog fragment again the previous state of the radio button will be automatically selected. However, after selecting another radio button the saved radio button will remain checked so I always have 2 radio buttons selected at the sametime.

Is there a way to unselect this when selecting other radio buttons.

Here is my class when I dynamically create the radio buttons and add them to the radio group.

@EpoxyModelClass(layout = R.layout.list_item_sort_item)
abstract class SortItemModel(private val schedulersFacade: SchedulersFacade) : EpoxyBaseModel() {

    @EpoxyAttribute
    lateinit var listOfTopsProductSort: List<TopsProductSort>

    @EpoxyAttribute
    lateinit var tapSortButtonRelay: PublishRelay<TopsProductSort>

    override fun bind(holder: EpoxyBaseViewHolder) {
        with(holder.itemView) {
            rbSortOptionGroup.removeAllViews()

            listOfTopsProductSort.forEach { topsProductSort ->
                val materialRadioButton = MaterialRadioButton(context)

                materialRadioButton.setTextColor(ColorStateList.valueOf(ContextCompat.getColor(context, R.color.black)))
                materialRadioButton.text = topsProductSort.name
                materialRadioButton.tag = TopsProductSort(code = topsProductSort.code, name = topsProductSort.name)
               
                materialRadioButton.isChecked = topsProductSort.isSelected // Set the previous radio button that was selected

                materialRadioButton.clicks()
                    .debounce(250L, TimeUnit.MILLISECONDS)
                    .observeOn(schedulersFacade.ui)
                    .map {
                        materialRadioButton.tag as TopsProductSort
                    }
                    .subscribeBy(
                        onNext = { _topsProductSort ->
                            if (materialRadioButton.tag is TopsProductSort) {
                                tapSortButtonRelay.accept(_topsProductSort.copy(isSelected = true))
                            }
                        },
                        onError = {
                            Timber.e(it.localizedMessage)
                        }
                    )

                rbSortOptionGroup.addView(materialRadioButton)
            }
        }
    }

Solution

  • To avoid this behaviour when creating a MaterialRadioButton programmatically you need to set a Unique Identifier @+id for each MaterialRadioButton so that RadioGroup can identify each of its children during of its selection/unselection phase. For this purpose you can use the ViewCompat.generateViewId() to generate a Unique Id programmatically.

    During the creation of each MaterialRadioButton you can use the above method like below:

    val materialRadioButton = MaterialRadioButton(context)
    materialRadioButton.id = ViewCompat.generateViewId()
    

    By doing the above the RadioGroup will be able to uniquely identify each of its children and will act as a Single selection.