androidxmlkotlinradio-group

Custom radio group with a label in android (kotlin)


I am trying to create a custom radio group where I can add a label on top via an attribute (the sample below has a hardcoded label text, I did not include the attribute in this sample to keep things simple).

I currently have this code for the radio group:

class LabelRadioGroup(context: Context, attrs: AttributeSet?) : RadioGroup(context, attrs)
{
    init 
    {
        LayoutInflater.from(context).inflate(R.layout.compound_label_radiogroup, this, true)
    }
}

And this xml (compound_label_radiogroup):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

    <TextView
            android:id="@+id/labelTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Label" />

    <RadioGroup
            android:id="@+id/radioGroup"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" />

</LinearLayout>

Usage is like this:

 <sample.LabelRadioGroup
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:orientation="horizontal">
      <androidx.appcompat.widget.AppCompatRadioButton
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Radio button 1"/>
      <androidx.appcompat.widget.AppCompatRadioButton
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginStart="10dp"
              android:text="Radio button 2"/>
</sample.LabelRadioGroup>

The current code is definitely wrong, and adding a 'RadioGroup' in the LinearLayout doesn't look right. But I can't figure out how to do it in another way.

Right now the label is showing in front of the radio buttons. I would like the end result to be a reusable radio group that looks like this:

Label

O Radio button 1 O Radio button 2

To clarify, it needs to be reusable, so depending on usage the radio buttons in the group could be different. How could I do this?


Solution

  • This is just a guess at a strategy that might work. I didn't try it, and there might be some kinks that need to be worked out.

    Make your class extend LinearLayout instead of RadioGroup. After all, your XML template is a LinearLayout.

    In your class, override addView() so if the child view is a RadioButton, you add it to the child RadioGroup instead of the top level LinearLayout. Luckily, RadioGroup is also a LinearLayout, so the LayoutParams should be transferrable.

    The theory here is that when your XML that contains your class is being inflated, it will start calling addView for the nested RadioButtons, and your code will intercept and pass these views down into the RadioGroup to be grandchildren instead.

    class LabelRadioGroup(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs)
    {
        init {
            inflate(context, R.layout.compound_label_radiogroup, this)
        }
    
        private val radioGroup: RadioGroup = findViewById(R.id.radioGroup)
    
        override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {
            if (child is RadioButton) {
                radioGroup.addView(child, -1, params)
                return
            }
            super.addView(child, index, params)
        }
    
        // Add RadioGroup pass-through functions that you need, for example:
    
        fun setOnCheckedChangeListener(listener: RadioGroup.OnCheckedChangeListener) =
            radioGroup.setOnCheckedChangeListener(listener)
    
        
    }