简体   繁体   中英

How to sort a ListPreference by the display value

I am creating preferences from an XML file. 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.

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. 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. 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 . 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. 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

<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

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

<?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

<?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

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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