简体   繁体   English

如何按显示值对 ListPreference 进行排序

[英]How to sort a ListPreference by the display value

I am creating preferences from an XML file.我正在从 XML 文件创建首选项。 The following code, called from onCreate() of the preferences activity should sort the display items alphabetically according to the label being shown to the user.从首选项活动的onCreate()调用的以下代码应根据向用户显示的标签按字母顺序对显示项进行排序。

private void sortListPreferenceByEntries(String preferenceKey) {
  ListPreference preference = (ListPreference) findPreference(preferenceKey);
  Iterator<CharSequence> labels = Arrays.asList(preference.getEntries()).iterator();
  Iterator<CharSequence> keys = Arrays.asList(preference.getEntryValues()).iterator();
  Collator sortRules = Collator.getInstance(getResources().getConfiguration().locale);
  sortRules.setStrength(Collator.PRIMARY);
  TreeMap<CharSequence, CharSequence> sorter = new TreeMap<>(sortRules);
  int size = 0;
  while (labels.hasNext() && keys.hasNext()) {
    sorter.put(labels.next(), keys.next());
    size++;
  }
  CharSequence[] sortedLabels = new CharSequence[size];
  CharSequence[] sortedValues = new CharSequence[size];
  Iterator<Map.Entry<CharSequence, CharSequence>> entryIterator = sorter.entrySet().iterator();
  if (entryIterator.hasNext()) {
    Map.Entry<CharSequence, CharSequence> entry = entryIterator.next();
    for (int i = 0; entryIterator.hasNext() && i < size; entry = entryIterator.next(), i++) {
      sortedLabels[i] = entry.getKey();
      sortedValues[i] = entry.getValue();
    }
  }
  preference.setEntries(sortedLabels);
  preference.setEntryValues(sortedValues);
}

The code runs fine in the debugger, the result arrays sortedLabels and sortedValues are being created and manual inspection shows that they are what I expected them to be.代码在调试器中运行良好,正在创建结果数组sortedLabelssortedValues ,手动检查表明它们是我期望的。 The options are also presented to the user in the desired sorted manner.这些选项也以所需的排序方式呈现给用户。 As long as no item from the list is selected everything works fine, but as soon as I select an item from the list a NullPointerException is thrown.只要没有选择列表中的项目,一切都可以正常工作,但是一旦我从列表中选择了一个项目,就会抛出NullPointerException No line of my written code is part of the stack trace.我编写的代码中没有一行是堆栈跟踪的一部分。

07-27 16:37:19.343    7589-7589/org.example.appName E/App-Name﹕ null
java.lang.NullPointerException
        at android.preference.ListPreference.findIndexOfValue(ListPreference.java:215)
        at android.preference.ListPreference.getValueIndex(ListPreference.java:224)
        at android.preference.ListPreference.getEntry(ListPreference.java:202)
        at android.preference.ListPreference.getSummary(ListPreference.java:148)
        at android.preference.Preference.onBindView(Preference.java:542)
        at android.preference.Preference.getView(Preference.java:472)
        at android.preference.PreferenceGroupAdapter.getView(PreferenceGroupAdapter.java:222)
        at android.widget.AbsListView.obtainView(AbsListView.java:2588)
        at android.widget.ListView.makeAndAddView(ListView.java:1840)
        at android.widget.ListView.fillDown(ListView.java:681)
        at android.widget.ListView.fillGap(ListView.java:645)
        at android.widget.AbsListView.trackMotionScroll(AbsListView.java:6535)
        at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3664)
        at android.widget.AbsListView.onTouchEvent(AbsListView.java:4492)
        at android.view.View.dispatchTouchEvent(View.java:7690)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2395)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2119)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2401)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2134)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2401)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2134)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2401)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2134)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2401)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2134)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2401)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2134)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2401)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2134)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2306)
        at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1575)
        at android.app.Activity.dispatchTouchEvent(Activity.java:2470)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2254)
        at android.view.View.dispatchPointerEvent(View.java:7888)
        at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3977)
        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3861)
        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5101)
        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5080)
        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5179)
        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
        at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
        at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:174)
        at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:5151)
        at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:5198)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
        at android.view.Choreographer.doCallbacks(Choreographer.java:591)
        at android.view.Choreographer.doFrame(Choreographer.java:559)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
        at android.os.Handler.handleCallback(Handler.java:725)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:176)
        at android.app.ActivityThread.main(ActivityThread.java:5365)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.jav

Without sorting the elements, the app is completely stable.没有对元素进行排序,该应用程序是完全稳定的。 Unlike other cases that I found with a likely stacktrace I do not try to use int s as entry values .与我在可能的堆栈跟踪中发现的其他情况不同,我不会尝试使用int作为条目值 What can be wrong?有什么问题? I could also verify that the new arrays being written back are of same length (118 entries in my case) as the arrays originally retrieved from the getters.我还可以验证写回的新数组是否与最初从 getter 中检索到的数组长度相同(在我的例子中为 118 个条目)。 I also tried coyping back the new display order into the original array like this:我还尝试将新的显示顺序复制回原始数组,如下所示:

System.arraycopy(sortedLabels, 0, preference.getEntries(), 0, sortedLabels.length);
System.arraycopy(sortedValues, 0, preference.getEntryValues(), 0, sortedValues.length);

But with the same result.但结果相同。 I really don't see what I miss.我真的看不出我想念什么。 Any help would be appreciated.任何帮助,将不胜感激。

Probably this question may be related but doesn't yet have an answer either.可能这个问题可能是相关的,但也没有答案。


EDIT: As requested, here is a full app which reproduces the error:编辑:根据要求,这是一个完整的应用程序,可以重现错误:

AndroidManifest.xml AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.example.appName">

    <application android:allowBackup="true" android:label="App-Name"
        android:icon="@mipmap/ic_launcher" android:theme="@android:style/Theme.Black">

        <activity
            android:name="org.example.appName.SettingsActivity"
            android:theme="@android:style/Theme.Black.NoTitleBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

java/org/example/appName/SettingsActivity.java java/org/example/appName/SettingsActivity.java

package org.example.appName;

import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.PreferenceActivity;
import android.util.Log;

import java.text.Collator;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

public class SettingsActivity extends PreferenceActivity {

  @Override
  @SuppressWarnings("deprecation")
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      @Override
      public void uncaughtException(Thread t, Throwable e) {
        Log.e("App-Name", e.getMessage(), e);
      }
    });
    addPreferencesFromResource(R.xml.preferences);
    sortListPreferenceByEntries("language");
  }

  private void sortListPreferenceByEntries(String preferenceKey) {
    ListPreference preference = (ListPreference) findPreference(preferenceKey);
    Iterator<CharSequence> labels = Arrays.asList(preference.getEntries()).iterator();
    Iterator<CharSequence> keys = Arrays.asList(preference.getEntryValues()).iterator();
    Collator sortRules = Collator.getInstance(getResources().getConfiguration().locale);
    sortRules.setStrength(Collator.PRIMARY);
    TreeMap<CharSequence, CharSequence> sorter = new TreeMap<>(sortRules);
    int size = 0;
    while (labels.hasNext() && keys.hasNext()) {
      sorter.put(labels.next(), keys.next());
      size++;
    }
    CharSequence[] sortedLabels = new CharSequence[size];
    CharSequence[] sortedValues = new CharSequence[size];
    Iterator<Map.Entry<CharSequence, CharSequence>> entryIterator = sorter.entrySet().iterator();
    if (entryIterator.hasNext()) {
      Map.Entry<CharSequence, CharSequence> entry = entryIterator.next();
      for (int i = 0; entryIterator.hasNext() && i < size; entry = entryIterator.next(), i++) {
        sortedLabels[i] = entry.getKey();
        sortedValues[i] = entry.getValue();
      }
    }
    preference.setEntries(sortedLabels);
    preference.setEntryValues(sortedValues);
  }
}

res/xml/preferences.xml res/xml/preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="caption">
        <ListPreference
            android:entries="@array/mode1_options"
            android:entryValues="@array/mode1_values"
            android:key="mode1"
            android:summary="Change mode 1"
            android:title="Mode 1" />
        <ListPreference
            android:entries="@array/mode2_options"
            android:entryValues="@array/mode2_values"
            android:key="mode2"
            android:summary="Change mode 2"
            android:title="Mode 2" />
        <ListPreference
            android:entries="@array/mode3_options"
            android:entryValues="@array/mode3_values"
            android:key="mode3"
            android:summary="Change mode 3"
            android:title="Mode 3" />
        <ListPreference
            android:entries="@array/mode4_options"
            android:entryValues="@array/mode4_values"
            android:key="mode4"
            android:summary="Change mode 4"
            android:title="Mode 4" />
        <ListPreference
            android:entries="@array/mode5_options"
            android:entryValues="@array/mode5_values"
            android:key="mode5"
            android:summary="Change mode 5"
            android:title="Mode 5" />
    </PreferenceCategory>
    <PreferenceCategory android:title="caption">
        <EditTextPreference
            android:key="input1"
            android:title="Input text" />
        <ListPreference
            android:entries="@array/mode6_options"
            android:entryValues="@array/mode6_values"
            android:key="mode6"
            android:summary="Change mode 6"
            android:title="Mode 6" />
        <CheckBoxPreference
            android:key="option1"
            android:summary="Change option 1"
            android:title="Option 1" />
        <CheckBoxPreference
            android:key="option2"
            android:summary="Change option 2"
            android:title="Option 2" />
        <ListPreference
            android:entries="@array/language_options"
            android:entryValues="@array/language_values"
            android:key="language"
            android:summary="To demonstrate the problem"
            android:title="Use this menu" />
        <CheckBoxPreference
            android:key="option3"
            android:summary="Change option 3"
            android:title="Option 3" />
    </PreferenceCategory>
    <PreferenceCategory android:title="caption">
        <CheckBoxPreference
            android:defaultValue="true"
            android:key="option4"
            android:summary="Change option 4"
            android:title="Option 4" />
        <CheckBoxPreference
            android:defaultValue="false"
            android:key="option5"
            android:summary="Change option 5"
            android:title="Option 5" />
    </PreferenceCategory>
</PreferenceScreen>

res/values/array.xml资源/值/array.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="mode1_options">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode1_values" translatable="false">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode2_options">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode2_values" translatable="false">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode3_options">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode3_values" translatable="false">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode4_options">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode4_values" translatable="false">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode5_options">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode5_values" translatable="false">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="language_options">
        <item>Oromo</item>
        <item>Afrikaans</item>
        <item>Albanian</item>
        <item>Asturian</item>
        <item>Aymara</item>
        <item>Azerbaijani, Latin script</item>
        <item>Balinese</item>
        <item>Basque</item>
        <item>Belarusian</item>
        <item>Bemba</item>
        <item>Bikol</item>
        <item>Bislama</item>
        <item>Bosnian, Cyrillic script</item>
        <item>Bosnian, Latin script</item>
        <item>Brazilian Portuguese</item>
        <item>Breton</item>
        <item>Bulgarian</item>
        <item>Catalan</item>
        <item>Chamorro</item>
        <item>Corsican</item>
        <item>Croatian</item>
        <item>Czech</item>
        <item>Danish</item>
        <item>Dutch</item>
        <item>English</item>
        <item>Esperanto</item>
        <item>Estonian</item>
        <item>Faeroese</item>
        <item>Fijian</item>
        <item>Finnish</item>
        <item>French</item>
        <item>Frisian</item>
        <item>Friulian</item>
        <item>Galician</item>
        <item>Luganda</item>
        <item>German</item>
        <item>Greek</item>
        <item>Greenlandic</item>
        <item>Haitian Creole</item>
        <item>Hani</item>
        <item>Hiligaynon</item>
        <item>Hungarian</item>
        <item>Icelandic</item>
        <item>Ido</item>
        <item>Ilocano</item>
        <item>Indonesian</item>
        <item>Interlingua</item>
        <item>Irish</item>
        <item>Italian</item>
        <item>Javanese</item>
        <item>Kapampangan</item>
        <item>Kazakh</item>
        <item>Kongo</item>
        <item>Kinyarwanda</item>
        <item>Kurdish</item>
        <item>Latin</item>
        <item>Latvian</item>
        <item>Lithuanian</item>
        <item>Luba-Kasai</item>
        <item>Luxembourgish</item>
        <item>Macedonian</item>
        <item>Madurese</item>
        <item>Malagasy</item>
        <item>Malay</item>
        <item>Maltese</item>
        <item>Manx</item>
        <item>Maori</item>
        <item>Mayan</item>
        <item>Mexican Spanish</item>
        <item>Minangkabau</item>
        <item>Mongolian</item>
        <item>Nahuatl</item>
        <item>none</item>
        <item>Norwegian</item>
        <item>Nyanja</item>
        <item>Nynorsk</item>
        <item>Papiamento</item>
        <item>Nigerian Pidgin</item>
        <item>English-based pidgin</item>
        <item>Polish</item>
        <item>Portuguese</item>
        <item>Provençal</item>
        <item>Quechua</item>
        <item>Rhaeto-Romance</item>
        <item>Romanian</item>
        <item>Rundi</item>
        <item>Russian</item>
        <item>Samoan</item>
        <item>Sardinian</item>
        <item>Scottish Gaelic</item>
        <item>Serbian, Cyrillic script</item>
        <item>Serbian, Latin script</item>
        <item>Shona</item>
        <item>Slovak</item>
        <item>Slovene</item>
        <item>Somali</item>
        <item>Sotho</item>
        <item>Spanish</item>
        <item>Sundanese</item>
        <item>Swahili</item>
        <item>Swedish</item>
        <item>Swiss German</item>
        <item>Tagalog</item>
        <item>Tahitian</item>
        <item>Tatar</item>
        <item>Tetum</item>
        <item>Tongan</item>
        <item>Tswana</item>
        <item>Turkish</item>
        <item>Turkmen</item>
        <item>Ukrainian</item>
        <item>Uzbek, Latin script</item>
        <item>Waray-Waray</item>
        <item>Welsh</item>
        <item>Wolof</item>
        <item>Xhosa</item>
        <item>Zapotec</item>
        <item>Zulu</item>
    </string-array>
    <string-array name="language_values" translatable="false">
        <item>Afaan</item>
        <item>Afrikaans</item>
        <item>Albanian</item>
        <item>Asturian</item>
        <item>Aymara</item>
        <item>Azeri_Latin</item>
        <item>Balines</item>
        <item>Basque</item>
        <item>Belrusian</item>
        <item>Bemba</item>
        <item>Bikol</item>
        <item>Bislama</item>
        <item>Bosnian_Cyrillic</item>
        <item>Bosnian_Latin</item>
        <item>Brazilian</item>
        <item>Breton</item>
        <item>Bulgarian</item>
        <item>Catalan</item>
        <item>Chamorro</item>
        <item>Corsican</item>
        <item>Croatian</item>
        <item>Czech</item>
        <item>Danish</item>
        <item>Dutch</item>
        <item>English</item>
        <item>Esperanto</item>
        <item>Estonian</item>
        <item>Faeroese</item>
        <item>Fijian</item>
        <item>Finnish</item>
        <item>French</item>
        <item>Frisian</item>
        <item>Friulian</item>
        <item>Galician</item>
        <item>Ganda</item>
        <item>German</item>
        <item>Greek</item>
        <item>Greenlandic</item>
        <item>Haitian_Creole</item>
        <item>Hani</item>
        <item>Hiligaynon</item>
        <item>Hungarian</item>
        <item>Icelandic</item>
        <item>Ido</item>
        <item>Ilocano</item>
        <item>Indonesian</item>
        <item>Interlingua</item>
        <item>Irish_Gaelic</item>
        <item>Italian</item>
        <item>Javanese</item>
        <item>Kapampangan</item>
        <item>Kazakh</item>
        <item>Kicongo</item>
        <item>Kinyarwanda</item>
        <item>Kurdish</item>
        <item>Latin</item>
        <item>Latvian</item>
        <item>Lithuanian</item>
        <item>Luba</item>
        <item>Luxemb</item>
        <item>Macedonian</item>
        <item>Madurese</item>
        <item>Malagasy</item>
        <item>Malay</item>
        <item>Maltese</item>
        <item>Manx</item>
        <item>Maori</item>
        <item>Mayan</item>
        <item>Mexican</item>
        <item>Minankabaw</item>
        <item>Mongol</item>
        <item>Nahuatl</item>
        <item>None</item>
        <item>Norwegian</item>
        <item>Nyanja</item>
        <item>Nynorsk</item>
        <item>Papiamento</item>
        <item>Pidgin_nigeria</item>
        <item>PidginEnglish</item>
        <item>Polish</item>
        <item>Portuguese</item>
        <item>Provencal</item>
        <item>Quechua</item>
        <item>Rhaeto_Roman</item>
        <item>Romanian</item>
        <item>Rundi</item>
        <item>Russian</item>
        <item>Samoan</item>
        <item>Sardinian</item>
        <item>Scottish_Gaelic</item>
        <item>Serbian</item>
        <item>Serbian_Latin</item>
        <item>Shona</item>
        <item>Slovak</item>
        <item>Slovenian</item>
        <item>Somali</item>
        <item>Sotho</item>
        <item>Spanish</item>
        <item>Sundanese</item>
        <item>Swahili</item>
        <item>Swedish</item>
        <item>Swiss_German</item>
        <item>Tagalog</item>
        <item>Tahitian</item>
        <item>Tatar</item>
        <item>Tetum</item>
        <item>Tongan</item>
        <item>Tswana</item>
        <item>Turkish</item>
        <item>Turkmen</item>
        <item>Ukrainian</item>
        <item>Uzbek_Latin</item>
        <item>Waray</item>
        <item>Welsh</item>
        <item>Wolof</item>
        <item>Xhosa</item>
        <item>Zapotec</item>
        <item>Zulu</item>
    </string-array>
    <string-array name="mode6_options">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
    <string-array name="mode6_values" translatable="false">
        <item>Option 1</item>
        <item>Option 2</item>
        <item>Option 3</item>
    </string-array>
</resources>

After app starts, scroll down to menu entry “Use this menu”.应用程序启动后,向下滚动到菜单项“使用此菜单”。 As you see, sorting works, first entry is “Afrikaans” (not “Oromo”).如您所见,排序有效,第一个条目是“南非荷兰语”(不是“奥罗莫语”)。 Select any option of your choice, then either scroll around the menu or try to open the same menu entry again: Stacktrace arises.选择您选择的任何选项,然后滚动菜单或尝试再次打开相同的菜单条目:堆栈跟踪出现。 If you comment out the sorting function如果注释掉排序功能

// sortListPreferenceByEntries("language");

the menu works as expected and does not crash.菜单按预期工作并且不会崩溃。 So the open question remains: How to sort the menu in a way that the app continues to work (as with unsorted menu)?因此,悬而未决的问题仍然存在:如何以应用程序继续工作的方式对菜单进行排序(与未排序的菜单一样)?

your i in你的i

for (int i = 0; entryIterator.hasNext() && i < size; entry = entryIterator.next(), i++) {

is wrong.是错的。 if you have eg size of 15 , you in fact iterate over 0 to 13 , last one ( 14 ) is left null .如果你有例如15 size ,你实际上迭代013 ,最后一个 ( 14 ) 是null

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM