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.