Skip to content

Instantly share code, notes, and snippets.

@cardil
Last active January 31, 2022 10:49
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save cardil/4754571 to your computer and use it in GitHub Desktop.
Save cardil/4754571 to your computer and use it in GitHub Desktop.
MultiSelectListPreference for devices running Android in the API earlier than level 11. Support ChangeListener receiving list of selected values. Supports automatically setting of summary. Examples attached.
private static OnPreferenceChangeListener autoOnChangeListener = new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference rawPreference, Object newValue) {
List<CharSequence> selected = Arrays.asList((CharSequence[]) newValue);
if (selected.contains("1")) {
// do some work
}
return true;
}
};
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 = "\u0001\u0007\u001D\u0007\u0001";
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 || entryValues == null
|| entries.length != entryValues.length) {
throw new IllegalStateException(
"MultiSelectListPreference requires an entries array and an entryValues "
+ "array which are both the same length");
}
restoreCheckedEntries();
OnMultiChoiceClickListener listener = new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean val) {
entryChecked[which] = val;
}
};
builder.setMultiChoiceItems(entries, entryChecked, listener);
}
private CharSequence[] unpack(CharSequence val) {
if (val == null || "".equals(val)) {
return new CharSequence[0];
} else {
return ((String) val).split(separator);
}
}
/**
* 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 || (!(oIter = iterable.iterator()).hasNext()))
return "";
StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next()));
while (oIter.hasNext())
oBuilder.append(separator).append(oIter.next());
return oBuilder.toString();
}
}
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<pl.wavesoftware.widget.MultiSelectListPreference
android:defaultValue="@array/pref_autoon_defaults"
android:entries="@array/pref_autoon_titles"
android:entryValues="@array/pref_autoon_values"
android:key="autoon"
android:persistent="true"
android:title="@string/pref_autoon_events_title" />
</PreferenceScreen>
<resources>
<string-array name="pref_autoon_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
<string-array name="pref_autoon_titles">
<item>If the phone rings</item>
<item>While traveling</item>
<item>While charging</item>
<item>Periodically checks</item>
</string-array>
<string-array name="pref_autoon_defaults">
<item>2</item>
<item>3</item>
</string-array>
</resources>
@Garyteck
Copy link

@ercillagorka, the ExamplePartOfActivy has a listener autoChangeListener, and a Preference Object mMultiSelectListPreference . This object will listen to any changes to the preference object mMultiSelectListPreference. If they are some changes in the preference object, you might want to do something

//// ExamplePartOfActivity.java

MultiSelectListPreference mMultiSelectListPreference = new MultiSelectListPreference();
mMultiSelectListPreference.setOnPreferenceChangeListener(autoOnChangeListener) ;

private static OnPreferenceChangeListener autoOnChangeListener = new OnPreferenceChangeListener() {
@OverRide
public boolean onPreferenceChange(Preference rawPreference, Object newValue) {
List selected = Arrays.asList((CharSequence[]) newValue);
if (selected.contains("1")) {
// If they are some changes in the preference object, you might want to do something !
}
return true;
}
};

@Takha25
Copy link

Takha25 commented Aug 27, 2017

Line 79 produces the following error:
E/InputEventReceiver: Exception dispatching input event.
E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback
E/MessageQueue-JNI: java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at MyMultiSelectListPreference.restoreCheckedEntries(MyMultiSelectListPreference.java:81)
at MyMultiSelectListPreference.onPrepareDialogBuilder(MyMultiSelectListPreference.java:44)
at android.preference.DialogPreference.showDialog(DialogPreference.java:306)
at android.preference.DialogPreference.onClick(DialogPreference.java:277)
at android.preference.Preference.performClick(Preference.java:994)
at android.preference.PreferenceScreen.onItemClick(PreferenceScreen.java:249)
at android.widget.AdapterView.performItemClick(AdapterView.java:310)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment