MultiSelectListPreference example

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



MultiSelectListPreference example



I am having a hard time trying to find a good example of the MultiSelectListPreference provided in the Android API. I have seen many references to this blog, and tho this is the end result I desire, I don't want to create a class for each multi-select preference I want to implement.
Ultimately I want to see the preferences xml for a simple multi-select dialog (which i will populate the values for dynamically), as well to the call to addPreferencesFromResource(R.xml.preferences);


addPreferencesFromResource(R.xml.preferences);



Currently, I have:


<MultiSelectListPreference
android:defaultValue=""
android:enabled="true"
android:entries="@array/pref_default_entries"
android:entryValues="@array/pref_default_values"
android:key="TargetList"
android:persistent="true"
android:summary="@string/TargetSummary"
android:title="@string/TargetTitle" />



and when I try to call addPreferencesFromResource in my Activities onCreate call I am getting the following error:


06-18 13:59:30.690: E/AndroidRuntime(6052): FATAL EXCEPTION: main
06-18 13:59:30.690: E/AndroidRuntime(6052): java.lang.RuntimeException: Unable to start activity ComponentInfocom.tracker/com.tracker.TrackerActivity: android.view.InflateException: Binary XML file line #37: Error inflating class java.lang.reflect.Constructor
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1818)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1834)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.ActivityThread.access$500(ActivityThread.java:122)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1027)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.os.Handler.dispatchMessage(Handler.java:99)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.os.Looper.loop(Looper.java:132)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.ActivityThread.main(ActivityThread.java:4126)
06-18 13:59:30.690: E/AndroidRuntime(6052): at java.lang.reflect.Method.invokeNative(Native Method)
06-18 13:59:30.690: E/AndroidRuntime(6052): at java.lang.reflect.Method.invoke(Method.java:491)
06-18 13:59:30.690: E/AndroidRuntime(6052): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
06-18 13:59:30.690: E/AndroidRuntime(6052): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
06-18 13:59:30.690: E/AndroidRuntime(6052): at dalvik.system.NativeStart.main(Native Method)
06-18 13:59:30.690: E/AndroidRuntime(6052): Caused by: android.view.InflateException: Binary XML file line #37: Error inflating class java.lang.reflect.Constructor
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.createItem(GenericInflater.java:397)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.onCreateItem(GenericInflater.java:417)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.createItemFromTag(GenericInflater.java:428)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.rInflate(GenericInflater.java:481)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.inflate(GenericInflater.java:326)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.inflate(GenericInflater.java:263)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:269)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.PreferenceActivity.addPreferencesFromResource(PreferenceActivity.java:1366)
06-18 13:59:30.690: E/AndroidRuntime(6052): at com.tracker.TrackerActivity.onCreate(TrackerActivity.java:30)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1050)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1782)
06-18 13:59:30.690: E/AndroidRuntime(6052): ... 11 more
06-18 13:59:30.690: E/AndroidRuntime(6052): Caused by: java.lang.reflect.InvocationTargetException
06-18 13:59:30.690: E/AndroidRuntime(6052): at java.lang.reflect.Constructor.constructNative(Native Method)
06-18 13:59:30.690: E/AndroidRuntime(6052): at java.lang.reflect.Constructor.newInstance(Constructor.java:416)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.GenericInflater.createItem(GenericInflater.java:383)
06-18 13:59:30.690: E/AndroidRuntime(6052): ... 21 more
06-18 13:59:30.690: E/AndroidRuntime(6052): Caused by: java.lang.NullPointerException
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.content.res.AssetManager.getResourceTextArray(AssetManager.java:215)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.content.res.Resources.getTextArray(Resources.java:435)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.content.res.TypedArray.getTextArray(TypedArray.java:628)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.MultiSelectListPreference.onGetDefaultValue(MultiSelectListPreference.java:210)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.Preference.<init>(Preference.java:257)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.DialogPreference.<init>(DialogPreference.java:69)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.DialogPreference.<init>(DialogPreference.java:90)
06-18 13:59:30.690: E/AndroidRuntime(6052): at android.preference.MultiSelectListPreference.<init>(MultiSelectListPreference.java:49)
06-18 13:59:30.690: E/AndroidRuntime(6052): ... 24 more



I look forward to your reply!





Why take so much trouble. In your fragment "onCreate" get the preference using "findPreference" and use method "setEntries/setEntryValues" to set dynamically.
– Ishaan
Aug 2 '16 at 8:31




4 Answers
4



Sigrist is correct, to solve the initial error you are seeing. It needs default values provided, even if its empty. This helped me in that I want to provide values at RUNTIME, but not mess with the full implementation.



See this code for how I provide values at runtime, while not having to handle the full implementation.


public class CalendarListPreference extends MultiSelectListPreference

ContentResolver cr;
Cursor cursor;
String projection = new String CalendarContract.Calendars.NAME, CalendarContract.Calendars.CALENDAR_DISPLAY_NAME;
String selection = "(" + CalendarContract.Calendars.VISIBLE + " = ?)";
String selectionArgs = new String "1" ;

public CalendarListPreference(Context context, AttributeSet attrs)
super(context, attrs);

List<CharSequence> entries = new ArrayList<CharSequence>();
List<CharSequence> entriesValues = new ArrayList<CharSequence>();

cr = context.getContentResolver();
cursor = cr.query(CalendarContract.Calendars.CONTENT_URI, projection, selection, selectionArgs, null);

while (cursor.moveToNext())
String name = cursor.getString(0);
String displayName = cursor.getString(1);

entries.add(name);
entriesValues.add(displayName);


setEntries(entries.toArray(new CharSequence));
setEntryValues(entriesValues.toArray(new CharSequence));




In my strings.xml


<string-array name="pref_calendar_list_default">
</string-array>



In my preferences.xml


<com.mynameistodd.autovolume.CalendarListPreference
android:defaultValue="@array/pref_calendar_list_default"
android:key="@string/pref_calendar_list_key"
android:summary="@string/pref_calendar_list_summary"
android:title="@string/pref_calendar_list_title"
android:dependency="@string/pref_calendar_enabled_key"/>



I know this is a bit of an old question, but it helped me, so here is my answer!



You need to specify the defaultValues property


<MultiSelectListPreference
android:dialogTitle="@string/mode_repeat"
android:key="mode_repeat"
android:summary=""
android:title="@string/mode_repeat"
android:entries="@array/weekdays"
android:entryValues="@array/weekdays_values"
android:defaultValue="@array/empty_array"
/>



If you don't want default values, create an empty array in your strings.xml


<string-array name="empty_array"/>





This is basically what the Android API suggests. I am interested in adding entries at RUNTIME. There is nothing dynamic about modifying strings.xml
– mohbandy
Oct 10 '13 at 16:30



I've created MultiSelectListPreference for devices running Android in the API earlier than level 11.



https://gist.github.com/cardil/4754571


package pl.wavesoftware.widget;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.res.TypedArray;
import android.preference.ListPreference;
import android.util.AttributeSet;

public class MultiSelectListPreference extends ListPreference

private String separator;
private static final String DEFAULT_SEPARATOR = "u0001u0007u001Du0007u0001";
private boolean entryChecked;

public MultiSelectListPreference(Context context, AttributeSet attributeSet)
super(context, attributeSet);
entryChecked = new boolean[getEntries().length];
separator = DEFAULT_SEPARATOR;


public MultiSelectListPreference(Context context)
this(context, null);


@Override
protected void onPrepareDialogBuilder(Builder builder)
CharSequence entries = getEntries();
CharSequence entryValues = getEntryValues();
if (entries == null

private CharSequence unpack(CharSequence val)
if (val == null

/**
* Gets the entries values that are selected
*
* @return the selected entries values
*/
public CharSequence getCheckedValues()
return unpack(getValue());


private void restoreCheckedEntries()
CharSequence entryValues = getEntryValues();

// Explode the string read in sharedpreferences
CharSequence vals = unpack(getValue());

if (vals != null)
List<CharSequence> valuesList = Arrays.asList(vals);
for (int i = 0; i < entryValues.length; i++)
CharSequence entry = entryValues[i];
entryChecked[i] = valuesList.contains(entry);




@Override
protected void onDialogClosed(boolean positiveResult)
List<CharSequence> values = new ArrayList<CharSequence>();

CharSequence entryValues = getEntryValues();
if (positiveResult && entryValues != null)
for (int i = 0; i < entryValues.length; i++)
if (entryChecked[i] == true)
String val = (String) entryValues[i];
values.add(val);



String value = join(values, separator);
setSummary(prepareSummary(values));
setValueAndEvent(value);



private void setValueAndEvent(String value)
if (callChangeListener(unpack(value)))
setValue(value);



private CharSequence prepareSummary(List<CharSequence> joined)
List<String> titles = new ArrayList<String>();
CharSequence entryTitle = getEntries();
CharSequence entryValues = getEntryValues();
int ix = 0;
for (CharSequence value : entryValues)
if (joined.contains(value))
titles.add((String) entryTitle[ix]);

ix += 1;

return join(titles, ", ");


@Override
protected Object onGetDefaultValue(TypedArray typedArray, int index)
return typedArray.getTextArray(index);


@Override
protected void onSetInitialValue(boolean restoreValue,
Object rawDefaultValue)
String value = null;
CharSequence defaultValue;
if (rawDefaultValue == null)
defaultValue = new CharSequence[0];
else
defaultValue = (CharSequence) rawDefaultValue;

List<CharSequence> joined = Arrays.asList(defaultValue);
String joinedDefaultValue = join(joined, separator);
if (restoreValue)
value = getPersistedString(joinedDefaultValue);
else
value = joinedDefaultValue;


setSummary(prepareSummary(Arrays.asList(unpack(value))));
setValueAndEvent(value);


/**
* Joins array of object to single string by separator
*
* Credits to kurellajunior on this post
* http://snippets.dzone.com/posts/show/91
*
* @param iterable
* any kind of iterable ex.: <code>["a", "b", "c"]</code>
* @param separator
* separetes entries ex.: <code>","</code>
* @return joined string ex.: <code>"a,b,c"</code>
*/
protected static String join(Iterable<?> iterable, String separator)
Iterator<?> oIter;
if (iterable == null






It's not possible to use dynamic entries loading with this class.
– James
Aug 29 '13 at 17:05



I got same error because I defined my array.xml with entries in values-large, but didn't have the file in the default values package. So I just moved array.xml into values.






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard