How to customize list preference radio button

11,873

Styling the ListPreference from XML is not directly possible. The problem is that ListPreference (through DialogPreference) calls AlertDialog.Builder(Context) to build its Dialog, rather than AlertDialog.Builder(Context context, int themeResourceId). While the latter allows for providing a theme, the former does not, causing it to fall back to a default Android theme.

For a project, I needed a ListPreference with a custom title-color, a custom radiobutton-style and a custom ListView-selector (basically, Holo in a different color). I solved this by extending ListPreference and overriding onPrepareDialogBuilder(Builder) and OnCreateDialogView() so I could use a custom ListView with a simple ArrayAdapter, rather than Dialog's built-in ListView (which doesn't have support for styling). I also had to override onDialogClosed() in order to set the right value to the preference.

In order to use it, all you have to do is replace the classname of the preference in your preferences.xml rom ListPreference to com.your.packagename.ThemedListPreference. Other than that, the implementation is identical to ListPreference.

public class ThemedListPreference extends ListPreference implements OnItemClickListener {

    public static final String TAG = "ThemedListPreference";

    private int mClickedDialogEntryIndex;

    private CharSequence mDialogTitle;

    public ThemedListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ThemedListPreference(Context context) {
        super(context);
    }

    @Override
    protected View onCreateDialogView() {
        // inflate custom layout with custom title & listview
        View view = View.inflate(getContext(), R.layout.dialog_settings_updatetime, null);

        mDialogTitle = getDialogTitle();
        if(mDialogTitle == null) mDialogTitle = getTitle();
        ((TextView) view.findViewById(R.id.dialog_title)).setText(mDialogTitle);

        ListView list = (ListView) view.findViewById(android.R.id.list);
        // note the layout we're providing for the ListView entries
        ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
                getContext(), R.layout.btn_radio,
                getEntries());

        list.setAdapter(adapter);
        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        list.setItemChecked(findIndexOfValue(getValue()), true);
        list.setOnItemClickListener(this);

        return view;
    }

    @Override
    protected void onPrepareDialogBuilder(Builder builder) {
        // adapted from ListPreference
        if (getEntries() == null || getEntryValues() == null) {
            // throws exception
            super.onPrepareDialogBuilder(builder);
            return;
        }

        mClickedDialogEntryIndex = findIndexOfValue(getValue());

        // .setTitle(null) to prevent default (blue)
        // title+divider from showing up
        builder.setTitle(null);

        builder.setPositiveButton(null, null);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        mClickedDialogEntryIndex = position;
        ThemedListPreference.this.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
        getDialog().dismiss();
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
            // adapted from ListPreference
        super.onDialogClosed(positiveResult);

        if (positiveResult && mClickedDialogEntryIndex >= 0
                && getEntryValues() != null) {
            String value = getEntryValues()[mClickedDialogEntryIndex]
                    .toString();
            if (callChangeListener(value)) {
                setValue(value);
            }
        }
    }
}

For my ListView items I used the layout below. Note that drawable/btn_radio_holo_light is an XML-drawable like the one in your android-sdk/platforms/android-x/data/res/drawable folder, only with references to different drawables.

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:checkMark="@drawable/btn_radio_holo_light"
    android:gravity="center_vertical"
    android:minHeight="@dimen/list_item_minheight"
    android:paddingLeft="@dimen/list_item_paddingLeft"
    android:paddingRight="@dimen/list_item_paddingLeft" />

For my Dialog layout (onCreateDialogView()), I used the following:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/dialog_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:textColor="@color/title_color"
        android:textSize="22sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="@color/divider_color" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:listSelector="@drawable/list_selector" />

</LinearLayout>
Share:
11,873
Niraj Gupta
Author by

Niraj Gupta

Updated on June 05, 2022

Comments

  • Niraj Gupta
    Niraj Gupta about 2 years

    I have customized all the radioButtons in my application but the radioButtons in the listPreference does not get customized.

    I have used this xml named btn_radio.xml

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

    This is the customRadioButton which extends the android custom radio button

    <style name="CustomRadioButton"    Parent="@android:style/Widget.CompoundButton.RadioButton">
        <item name="android:button">@drawable/btn_radio</item>
    </style>
    

    in the theme of my application I have done this changes

    <item name="android:radioButtonStyle">@style/CustomRadioButton</item>
        <item name="android:listChoiceIndicatorSingle">@style/CustomRadioButton</item>
    

    This changes customize all the radioButtons in my application except radioButtons in my ListPreference

  • Dory
    Dory almost 11 years
    what is CheckedTextView here,is it any custom view created. Could you please guide.
  • Dory
    Dory almost 11 years
    what should be in this R.layout.btn_radio layout.
  • Felix
    Felix over 10 years
    @NidhiGondhia, CheckedTextView is a standard Android widget. The code snippet that starts with <CheckedTextView... is what should be in R.layout.btn_radio. Instead of btn_radio a better name might be list_item.
  • Martin Pfeffer
    Martin Pfeffer over 9 years
    Thanks. I was searching for hours to get a proper workaround. This solution is the best I have ever seen. This question should be voted as "checked" :)
  • User9211
    User9211 about 3 years
    I thnk it no longer works. Can't override 'onCreateDialogView'. Using PreferenceFragmentCompat.