I have a OnPreferenceClickListener which should remove a specific Preference (with key preference_to_remove) from a PreferenceScreen.
The problem is that my solution works when preference_to_remove is not located inside a nested PreferenceScreen but does not work, when it is inside a nested screen AND the screen orientation changes. Before screen orientation changes, nested screens are working as expected, too.
The following code contains two version, one with a flat non-nested PreferenceScreen and the broken nested PreferenceScreen.
What is the reason that the nested version is not able to remove the Preference with key preference_to_remove after screen orientation changes? What would be a solution besides using only flat PreferenceScreens and Intents to start new PreferenceScreens as pseudo children?
PS: I am using PreferenceActivity for FroYo compatibility.
How to reproduce with Test-App
Open App → Click Flat-Button → Click preference_to_click which should remove preference_to_remove. → Orientation change → Click preference_to_click to remove preference_to_remove again. Preference removed? Success!
Open App → Click Subscreen-Button → Click Test → Now repeat the steps from the first test, but this time preference_to_remove will be not not removable after orientation changing.
pref_flat.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<Preference
android:key="preference_to_click"
android:persistent="false"
android:title="preference_to_click" />
<Preference
android:key="preference_to_remove"
android:title="preference_to_remove" />
</PreferenceScreen>
pref_subscreen.xml (Nested PreferenceScreen)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceScreen
android:key="subscreen"
android:persistent="false"
android:title="Test" >
<Preference
android:key="preference_to_click"
android:persistent="false"
android:title="preference_to_click" />
<Preference
android:key="preference_to_remove"
android:title="preference_to_remove" />
</PreferenceScreen>
</PreferenceScreen>
Diff of PrefFlatActivity.java and PrefSubscreenActivity.java
1c1
< public class PrefFlatActivity extends PreferenceActivity {
---
> public class PrefSubscreenActivity extends PreferenceActivity {
5,6c5,7
< public static final String PREFERENCE_TO_CLICK = "preference_to_click";
< public static final String PREFERENCE_TO_REMOVE = "preference_to_remove";
---
> private static final String PREFERENCE_TO_CLICK = PrefFlatActivity.PREFERENCE_TO_CLICK;
> private static final String PREFERENCE_TO_REMOVE = PrefFlatActivity.PREFERENCE_TO_REMOVE;
> private static final String PREFERENCE_SUBSCREEN = "subscreen";
15c16
< addPreferencesFromResource(R.xml.pref_flat);
---
> addPreferencesFromResource(R.xml.pref_subscreen);
28c29
< PreferenceScreen screen = getPreferenceScreen();
---
> PreferenceScreen screen = (PreferenceScreen) findPreference(PREFERENCE_SUBSCREEN);
PrefFlatActivity.java (Working)
/**
* Works as expected. Clicking toggles the "visibility" of the PREFERENCE_TO_REMOVE Preference.
*/
public class PrefFlatActivity extends PreferenceActivity {
/**
* Preference keys.
*/
public static final String PREFERENCE_TO_CLICK = "preference_to_click";
public static final String PREFERENCE_TO_REMOVE = "preference_to_remove";
private final String PREF_NAME = getClass().getName() + ".pref";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPreferenceManager().setSharedPreferencesName(PREF_NAME);
addPreferencesFromResource(R.xml.pref_flat);
findPreference(PREFERENCE_TO_CLICK)
.setOnPreferenceClickListener(new OnFlatClickListener());
}
/**
* Removes or adds Preference with key PREFERENCE_TO_REMOVE when clicked.
*/
private class OnFlatClickListener implements OnPreferenceClickListener {
private Preference mRescuedPreference;
public boolean onPreferenceClick(Preference preference) {
PreferenceScreen screen = getPreferenceScreen();
Preference prefToRemove = screen.findPreference(PREFERENCE_TO_REMOVE);
Log.d("test", "Found PREFERENCE_TO_REMOVE: " + (prefToRemove != null));
if (prefToRemove != null) {
screen.removePreference(prefToRemove);
mRescuedPreference = prefToRemove; // Rescue reference to re-add it later.
}
else {
screen.addPreference(mRescuedPreference);
}
return true;
}
}
}
PrefSubscreenActivity.java (Nested, broken after orientation change)
/**
* Broken after orientation change. Clicking does not remove/add PREFERENCE_TO_REMOVE.
*/
public class PrefSubscreenActivity extends PreferenceActivity {
/**
* Preference keys.
*/
private static final String PREFERENCE_TO_CLICK = PrefFlatActivity.PREFERENCE_TO_CLICK;
private static final String PREFERENCE_TO_REMOVE = PrefFlatActivity.PREFERENCE_TO_REMOVE;
private static final String PREFERENCE_SUBSCREEN = "subscreen";
private final String PREF_NAME = getClass().getName() + ".pref";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPreferenceManager().setSharedPreferencesName(PREF_NAME);
addPreferencesFromResource(R.xml.pref_subscreen);
findPreference(PREFERENCE_TO_CLICK)
.setOnPreferenceClickListener(new OnFlatClickListener());
}
/**
* Removes or adds Preference with key PREFERENCE_TO_REMOVE when clicked.
*/
private class OnFlatClickListener implements OnPreferenceClickListener {
private Preference mRescuedPreference;
public boolean onPreferenceClick(Preference preference) {
PreferenceScreen screen = (PreferenceScreen) findPreference(PREFERENCE_SUBSCREEN);
Preference prefToRemove = screen.findPreference(PREFERENCE_TO_REMOVE);
Log.d("test", "Found PREFERENCE_TO_REMOVE: " + (prefToRemove != null));
if (prefToRemove != null) {
screen.removePreference(prefToRemove);
mRescuedPreference = prefToRemove; // Rescue reference to re-add it later.
}
else {
screen.addPreference(mRescuedPreference);
}
return true;
}
}
}
edit: I’ve been having trouble getting this to work as you requested. I read through the source for
Preferences and how they handle state without any luck, so I’m probably missing something obvious (isn’t that how it often goes?)I did some testing, and I believe that the state of the
PreferenceScreenand/or theDialogit uses to display the nestedPreferenceScreenis behaving in a way that neither of us expects. This behavior is not observed when using aPreferenceCategoryinstead of aPreferenceScreen, for example.Since this way of working with
Preferences is deprecated, you can always try using fragments:However, I suspect you are avoiding this for legacy reasons.
Another option would be to create your
Preferenceprogrammatically:Building Preference screen in code depending on another setting
Or, you could handle configuration changes yourself:
Hopefully you get this figured out soon. I’m sure the solution will pop out at us eventually; it can’t be that hard! Here’s hoping 😉
Original source (mostly) retained below:
Recommended Reading: http://developer.android.com/guide/topics/resources/runtime-changes.html