多选列表设置示例

23
我很难找到一个好的MultiSelectListPreference示例,后者是安卓API提供的。我看到了许多参考此博客,尽管这是我想要的最终结果,但我不想为我想要实现的每个多重选择偏好创建一个类。 最终,我想查看一个简单的多选对话框的preferences xml(我将动态填充值),以及调用addPreferencesFromResource(R.xml.preferences);
目前,我的代码是:
<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" />

当我尝试在我的 Activity 的 onCreate 方法中调用 addPreferencesFromResource 方法时,我遇到了以下错误:

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 ComponentInfo{com.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

我期待你的回复!


为什么要费这么大的劲呢?在你的“onCreate”片段中,使用“findPreference”获取首选项,并使用“setEntries/setEntryValues”方法进行动态设置。 - Ishaan
5个回答

50

你需要指定 defaultValues 属性

<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"
        />

如果你不想要默认值,在你的strings.xml文件中创建一个空数组

<string-array name="empty_array"/>

4
这基本上是Android API建议的做法。我想在运行时添加条目。修改strings.xml并不具备动态性。 - mohbandy

12

Sigrist是正确的,要解决您看到的初始错误。即使是空的,它也需要提供默认值。这对我有帮助,因为我想在运行时提供值,但不干扰完整的实现。

查看此代码,了解我如何在运行时提供值,同时不必处理完整的实现。

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[]{}));
}
}

在我的strings.xml文件中

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

在我的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"/>

我知道这个问题有点老了,但它对我很有帮助,所以这是我的答案!


3

我已经为运行在Android API 11级之前的设备创建了MultiSelectListPreference。

  • 支持ChangeListener接收所选值列表。
  • 支持自动设置摘要。
  • 示例附带。

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 = "\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();
    }

}

2
这个类无法使用动态条目加载。 - Erveron

1

您可以在运行时通过设置值/条目来完成此操作,如下所示:

findPreference<MultiSelectListPreference>(getString(R.string.preference_key_some_apps))?.apply {
            val entryDisplay =
                listOf("Facebook Messenger", "Whatsapp", "Telegram", "SMS").toTypedArray()
            val entryValuesApps = listOf(
                Constants.FACEBOOK_MESSENGER_PACKAGE_NAME,
                Constants.WHATSAPP_PACKAGE_NAME,
                Constants.TELEGRAM_PACKAGE_NAME,
                Constants.SMS_MESSENGER_PACKAGE_NAME_HOLDER
            ).toTypedArray()

            entries = entryDisplay
            this.entryValues = entryValuesApps
        }

0
我遇到了同样的错误,因为我在 values-large 中定义了我的 array.xml 条目,但是在默认的 values 包中没有该文件。所以我只需将 array.xml 移动到 values 中即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接