MultiChoice Preference Widget for Android


Strangely, the Android framework does not include a widget which allows multiple selections in Preference Screens. I needed to add one of these for selecting folders to monitor for new media in PicPush and ended up having to implement it myself. If this is something you need for your Android app, following this tutorial will have you up and running in under 5 minutes.

Source code with example XML files can be downloaded from here.
Thanks to Mathieu Chauvinc for implementing XML attributes for separator and a check all option!

Step 1: Declare stylable attributes for check all and separator characters in attr.xml

A special XML attribute can be used to define which (if any) is the “Check all” button using the yournamespace:checkAll attribute in the preferences.xml.

A special XML attribute can be used to define the separator value you wish to use (for when the references are stored to disk on the device). Using yournamespace:separator the you can create their own choice of separator (without this the code will fall back to default)

In order to make use of the custom attributes in the layout XML file you must declare them as styleable in attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ListPreferenceMultiSelect">
    	<attr format="string" name="checkAll" />
    	<attr format="string" name="separator" />
    </declare-styleable>
</resources>

Step 2: Declare a multi-choice preference widget in your preferences XML file

The first step involves adding the appropriate XML to the file where your preference screen is defined. This can be done as so (You’ll have to change the undefined references to real @string constants):

<com.threefiftynice.android.preference.ListPreferenceMultiSelect android:defaultValue="#ALL#"
android:dependency="@string/pref_auto_scan_image"
    android:key="@string/pref_upload_buckets_image"
    android:title="Image folders to monitor" android:dialogTitle="Image folders to monitor"
    android:summary="Specify which folders should be monitored for images"
    android:entries="@array/pref_upload_buckets_default_entries"
    android:entryValues="@array/pref_upload_buckets_default_values"/>

Step 3: Declare supporting resources for the preferences attributes in

The preferences.xml file is referencing some string and array constants which we need to define in other resources

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string name="pref_upload_buckets_image">upload.buckets_image</string>
</resources>

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string-array name="pref_upload_buckets_default_entries">
		<item>All</item>
		<item>Test1 Display</item>
		<item>Test2 Display</item>
	</string-array>
	<string-array name="pref_upload_buckets_default_values">
		<item>#ALL#</item>
		<item>Test1 Value</item>
		<item>Test2 Value</item>
	</string-array>
</resources>

Step 4: Create Java class which implements the Multiple Choice List Preference

Now, of course, we need a Java class to back this up. The implementation is straightforward. It extends the ListPreference widget so it can inherit all the XML attributes recognized by it. From there, a few key methods are overridden which are explained below.

Step 4.1: Override onPrepareDialogBuilder (line 44)

onPrepareDialogBuilder is the method responsible for creating the popup dialog which contains all the choices to be displayed. The change from the base class here is simply to set the multi choice entries of the builder rather than the single choice entries. This takes care of the UI.

Step 4.2: Override onDialogClosed (line 69)

When the dialog is closed we need to build a special string to store in the SharedPreferences. The string is made up of the selected entries separated by a special separator string. The separator string i chose is quite weird, it has to be, to avoid conflicts with actual entries.

Step 4.3: Provide a static convenience method for parsing the stored value into a String array (line 62)

When a client reads back the stored value from the SharedPreferences it wont be very useful unless it can be parsed. This can be delegated to our custom implementation via a static method called parseStoredValue. This convenience method returns a String array which contains the user’s choices.

Step 5: You’re Done! Run the code!

Following the above steps should have you in good shape to get up and running. I implemented this code a few days ago, and am writing this tutorial from memory so i may have missed a few small details, however the main bones of the implementation is definitely contained here. Any problems/questions? Leave a comment below.

Walla!

Walla!

, , ,

  1. #1 by Lukas Hetzenecker at January 12th, 2012

    mysho, I had the same problem

    Replace

    mClickedDialogEntryIndices = new boolean[getEntries().length];

    with

    CharSequence[] entries = getEntries();
    mClickedDialogEntryIndices = new boolean[(entries != null) ? entries.length : 0];

  2. #2 by mysho at January 16th, 2012

    HI, I have next problem. When I open this multiselect preference and check some values everything works great, but when i reopen this preference nothing is selected. Where is problem ?
    thanks …

  3. #3 by T at January 19th, 2012

    Hi,
    when I use this with a full screen program, every time I click on a selection item it switches briefly out of fullscreen and back in.
    Any Idea how to solve that?
    Thanks.

    PS: Might be a bug with Android. The solution would be to add dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);
    Any idea where in the Java file I should add this?

  4. #4 by adrian at February 3rd, 2012

    Hi i am having the following issues:

    1) The changes still applied when i click cancel button.

    2) After i made some changes and close the app, when i reopen it, the checkbox are all unchecked again.

    I notice some people are having the same issue also. May i know how to solve it?

(will not be published)