There's already 20 answers, but dare I say I think I have the best one.
This uses view data binding, so the first thing you'll want to do is add this in your module's build.gradle
.
android {
dataBinding {
enabled = true
}
}
Then you can make a layout with your desired view hierarchy, for example:
fancy_radio_button.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="title" type="String"/>
<variable name="description" type="String"/>
<variable name="checked" type="boolean"/>
<variable name="buttonId" type="int"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<!-- clickable=false since we implement the click listener on the whole view -->
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:text="@{title}"
android:checked="@{checked}"/>
<TextView
android:text="@{description}"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:layout_width="match_parent"
android:layout_height="24dp" />
</LinearLayout>
</layout>
This can look however you want, just make sure to set android:clickable="false"
on the RadioButton
, and use the data binding variables where needed.
That layout gets handled by this class:
FancyRadioGroup.java
package com.example.app;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RadioGroup;
import com.example.app.FancyRadioButtonBinding;
import java.util.ArrayList;
public class FancyRadioGroup extends RadioGroup implements View.OnClickListener {
private final ArrayList<FancyRadioButtonBinding> radioButtons = new ArrayList<>();
private OnSelectionChangedListener selectionChangedListener;
public FancyRadioGroup(Context context) {
super(context);
}
public FancyRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setOnSelectionChangedListener(OnSelectionChangedListener selectionChangedListener) {
this.selectionChangedListener = selectionChangedListener;
}
public int addOption(String title, String description) {
// inflate view and get binding
FancyRadioButtonBinding buttonBinding = FancyRadioButtonBinding.inflate(LayoutInflater.from(getContext()), this, true);
// set title and description
buttonBinding.setTitle(title);
buttonBinding.setDescription(description);
// give the button an id (just use the index)
buttonBinding.setButtonId(radioButtons.size());
// set the root view's tag to the binding, so we can get the binding from the view
View root = buttonBinding.getRoot();
root.setTag(buttonBinding);
// set click listener on the whole view, so we can click anywhere
root.setOnClickListener(this);
radioButtons.add(buttonBinding);
// return button id to caller, so they know what was clicked
return buttonBinding.getButtonId();
}
@Override
public void onClick(View v) {
for (FancyRadioButtonBinding binding : radioButtons) {
binding.setChecked(v.getTag() == binding);
}
if (selectionChangedListener != null) {
selectionChangedListener.onSelectionChanged(getSelected());
}
}
public int getSelected() {
for (FancyRadioButtonBinding binding : radioButtons) {
if (binding.getChecked()) {
return binding.getButtonId();
}
}
return -1;
}
public interface OnSelectionChangedListener {
void onSelectionChanged(int buttonId);
}
}
To use, simply add the FancyRadioGroup
to your view:
activity_foo.xml
<com.example.app.FancyRadioGroup android:id="@+id/radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
And then add your options:
FooActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FancyRadioGroup radioGroup = findViewById(R.id.radio_group);
radioGroup.addOption("The First One", "This option is recommended for users who like the number one.");
radioGroup.addOption("The Second One", "For advanced users. Larger than one.");
radioGroup.setOnSelectionChangedListener(this::doSomething);
}
private void doSomething(int id) {
Toast.makeText(this, "selected: "+id, Toast.LENGTH_SHORT).show();
}