Replace Drawable in RadioButton
I found a way to use the native RadioButton
. You have to create your own Drawable and Style and then you're good to go. It took me all afternoon to get it right, so dear poor soul reading this - I hope it helps.
Below is a list of all resources you would need to achieve the following:
- Built on Android 13 (API level 33)
- Tested to also work on Android 7.0 (API level 24)
drawable/colour_picker.xml
(start with the selector
element if you don't care about the ripple effect when picking an option)
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@android:color/white" android:radius="50dp">
<item>
<selector>
<item android:drawable="@drawable/colour_picker_checked" android:state_checked="true" />
<item android:drawable="@drawable/colour_picker_unchecked" android:state_checked="false" />
</selector>
</item>
</ripple>
drawable/colour_picker_checked.xml
(make sure shapes only use tint
instead of color
here or they will later on be broken)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval" android:tint="#FFFFFF">
<stroke android:width="5dp" />
<solid android:color="@android:color/transparent" />
<size android:width="50dp" android:height="50dp"/>
</shape>
</item>
<item android:top="10dp" android:bottom="10dp" android:left="10dp" android:right="10dp">
<shape android:shape="oval" android:tint="#FFFFFF">
<solid android:width="1dp" />
</shape>
</item>
</layer-list>
drawable/colour_picker_unchecked.xml
(size needs to match colour_picker_checked.xml
exactly or selecting options causes layout shift)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval" android:tint="#FFFFFF">
<solid android:color="@android:color/transparent" />
<size android:width="50dp" android:height="50dp"/>
</shape>
</item>
<item android:top="10dp" android:bottom="10dp" android:left="10dp" android:right="10dp">
<shape android:shape="oval" android:tint="#FFFFFF">
<solid android:width="1dp" />
</shape>
</item>
</layer-list>
values/styles.xml
(the settings will distribute and align the buttons horizontally)
<style name="colour_picker"
parent="Base.Widget.AppCompat.CompoundButton.RadioButton">
<item name="android:button">@drawable/colour_picker</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">0dp</item>
<item name="android:gravity">center</item>
<item name="android:layout_weight">1</item>
</style>
layout/colour_picker.xml
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:orientation="vertical"
android:theme="@style/RippleStyle">
<RadioGroup
android:id="@+id/colour_choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp">
<RadioButton name="" style="@style/colour_picker" android:buttonTint="#FFFFFF"/>
<RadioButton style="@style/colour_picker" android:buttonTint="#FF00FF"/>
</RadioGroup>
</LinearLayout>
How to Use It
The only difficulty at this point is identifying which option was chosen. If your list of colours is fixed and you don't mind hardcoding, consider giving each RadioButton
an android:id
directly in the XML.
In my case, I chose to create the RadioButton
objects programmatically, which lets me cross-reference the colour from a string-array
resource.
String[] colourPalette = getResources().getStringArray(R.array.colour_options);
RadioGroup colourPicker = findViewById(R.id.YOUR_LAYOUT_WHERE_THIS_SHOULD_END_UP);
LayoutInflater inflater = LayoutInflater.from(this);
for (int i = 0; i < colourPalette.length; i++) {
RadioButton colourOption = (RadioButton) inflater.inflate(R.layout.colour_picker_item,null).getRootView();
colourOption.setId(i);
colourOption.setButtonTintList(ColorStateList.valueOf(Color.parseColor(colourPalette[i])));
colourPicker.addView(colourOption);
}
For this to work you need two additional resources:
layout/colour_picker_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RadioButton style="@style/colour_picker" />
values/colors.xml
<string-array name="colour_options">
<item name="#FFFF00">#FFFF00</item>
<item name="#FF0000">#FF0000</item>
</string-array>
And then in your code it's as easy as this:
colourPicker.setOnCheckedChangeListener((group, checkedId) -> {
RadioButton colourOption = colourPicker.findViewById(checkedId);
int colour = Color.parseColor(colourPalette[colourOption.getId()]);
// do whatever you need to do with your picked colour
});
custom radio button
or useimageView
orimageButton
orbutton
withdrawable
. – Stuart