Как отсортировать ListPreference по отображаемому значению

Я создаю настройки из файла XML. Следующий код, вызываемый из 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);
}

Код отлично работает в отладчике, массивы результатов sortedLabels и sortedValues создаются, и ручная проверка показывает, что они именно такие, как я ожидал. Опции также представлены пользователю в нужной сортировке. Пока ни один элемент из списка не выбран, все работает нормально, но как только я выбираю элемент из списка, выдается NullPointerException. Ни одна строка моего написанного кода не является частью трассировки стека.

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

Без сортировки элементов приложение полностью стабильно. В отличие от других случаев, которые я обнаружил с вероятной трассировкой стека, я не пытаюсь использовать ints в качестве входных значений. Что может быть не так? Я также мог бы убедиться, что новые массивы, записываемые обратно, имеют ту же длину (118 записей в моем случае), что и массивы, первоначально извлеченные из геттеров. Я также попытался скопировать новый порядок отображения в исходный массив следующим образом:

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

Но с тем же результатом. Я действительно не вижу, чего мне не хватает. Любая помощь будет оценена по достоинству.

Вероятно, этот вопрос может быть связан, но ответа на него пока нет.


РЕДАКТИРОВАТЬ: В соответствии с запросом, вот полное приложение, которое воспроизводит ошибку:

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>

После запуска приложения прокрутите вниз до пункта меню «Использовать это меню». Как видите, сортировка работает, первая запись — «африкаанс» (не «оромо»). Выберите любой вариант по вашему выбору, затем либо прокрутите меню, либо попробуйте снова открыть тот же пункт меню: возникает трассировка стека. Если вы закомментируете функцию сортировки

// sortListPreferenceByEntries("language");

меню работает как положено и не вылетает. Таким образом, остается открытым вопрос: как отсортировать меню таким образом, чтобы приложение продолжало работать (как в случае с несортированным меню)?


person Matthias Ronge    schedule 27.07.2015    source источник
comment
Включите код, который вызывает ошибку, как еще вы ожидаете, что мы ее отладим?   -  person Marcus    schedule 27.07.2015
comment
@Маркус, можешь воспроизвести проблему?   -  person Matthias Ronge    schedule 28.07.2015
comment
Я провел небольшое исследование, это, этот и этот вопрос предполагают, что исключение выбрасывается из-за того, что ваши массивы не относятся к типу string-array. Но, изучив ваш код, я могу сказать, что они есть. Я помню, как у меня была эта проблема некоторое время назад, когда я работал над одним из своих приложений. Не могли бы вы попытаться аннулировать кеш AS?   -  person Marcus    schedule 28.07.2015
comment
Если я правильно понял вашу идею, похоже, она тоже не работает. Кстати, теперь я использую обходной путь, который оставляет входные значения без изменений.   -  person Matthias Ronge    schedule 29.07.2015


Ответы (1)


твой i в

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

неправильно. если у вас есть, например. size из 15, вы фактически выполняете итерацию от 0 до 13, последний (14) остается null.

person hundreAd    schedule 30.04.2020