Search code examples
androidkotlinradio-buttonandroid-drawable

Radiobutton drawable not reacting to state_checked


I am trying to create a check-mark in a RadioButtonthat disappears if that button is not selected. However, the custom check-mark only seems to react to state_checked if I put it in the android:button attribute of RadioButton. This is unexpected, because if I replace the custom check-mark by android:drawableRight="?android:attr/listChoiceIndicatorSingle", this works perfectly fine. Including the check-mark in drawableRight has my preference since setting the spacing on the android:button attribute is problematic.

Are there different settings that will make the check-mark behave as expected?

This is the current code:

<RadioGroup
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <RadioButton
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:button="@null"
            android:drawableRight="@drawable/period_selector"
            android:background="?android:selectableItemBackground"
            android:layoutDirection="rtl"
            android:layout_gravity="start"
            android:textAlignment="textStart"
            android:paddingVertical="16dp"
            android:paddingHorizontal="16dp"
            android:text="@string/period_selection_month"
            android:textSize="16sp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="?divider" />

        <RadioButton
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:button="@null"
            android:drawableRight="@drawable/period_selector"
            android:background="?android:selectableItemBackground"
            android:layoutDirection="rtl"
            android:layout_gravity="start"
            android:textAlignment="textStart"
            android:paddingVertical="16dp"
            android:paddingHorizontal="16dp"
            android:text="@string/period_selection_year"
            android:textSize="16sp" />
</RadioGroup>

with drawable/period_selector.xml as follows:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_check" android:state_checked="true" />
</selector>

To summarize:

  • A custom check-mark in drawableRight does not render, regardless of whether the button is checked.
  • Using the standard listChoiceIndicatorSingle in drawableRight does correctly respond to whether the button is selected
  • Using the custom check-mark in button does show the expected selection behavior of appearing and disappearing, but does not listen to spacing

Solution

  • Problem

    <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ic_check" android:state_checked="true" /> </selector>

    Your selector misses the unchecked state of the RadioButton, and therefore it sets the drawableRight to have no drawable. So, the default occupied size for the drawableRight is 0.

    When you change the button state to be checked; it tries to draw the ic_check to currently reserved size of the drawableRight which is 0; and hence you'd see nothing changed.

    Solution

    To fix this, you need to specify the unchecked state of the selector to some drawable.

    My trials to use @null or to using 0 size, but both fails with exceptions as the drawable is not specified or it's 0 in size.

    So, the one that fixed that is to use the same drawable as ic_check but with a transparent current.

    This is the default check mark drawable provided by Android Studio:

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:tint="@color/teal_700"
        android:viewportWidth="24"
        android:viewportHeight="24">
    
        <path
            android:fillColor="@color/teal_700"
            android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" 
    
    </vector>
    

    Then create another typical one with a transparent color instead: ic_check_empty.xml

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24"
        android:viewportHeight="24"
        android:tint="@android:color/transparent">
        <path
            android:fillColor="@android:color/transparent"
            android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
    </vector>
    

    And apply that to the selector:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="@drawable/ic_check" android:state_checked="true" />
        <item android:drawable="@drawable/ic_check_empty" android:state_checked="false" />
    </selector>
    

    Side Note: Using a View within a RadioGroup will raise an exception as it's not allowed here; if you need to add a separator between the RadionButtons, then you can use:

    <RadioGroup
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="?android:attr/dividerHorizontal"
        android:showDividers="middle"> 
    

    Or you can have a look here for other solutions.