How to show and hide preferences on Android dynamically?

33,319

Solution 1

From a PreferenceActivity call

Preference somePreference = findPreference(SOME_PREFERENCE_KEY);
PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.removePreference(somePreference);

you can later call:

preferenceScreen.addPreference(somePreference);

The only a little bit tricky part is getting the order correct when adding back in. Look at PreferenceScreen documentation, particularly it's base class, PreferenceGroup for details.

Note: The above will only work for immediate children of a PreferenceScreen. If there is a PreferenceCategory in between, you need to remove the preference from its parent PreferenceCategory, not the PreferenceScreen. First to ensure the PreferenceCategory has an android:key attribute set in the XML file. Then:

Preference somePreference = findPreference(SOME_PREFERENCE_KEY);
PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference(SOME_PREFERENCE_CATEGORY_KEY);
preferenceCategory.removePreference(somePreference);

and:

preferenceCategory.addPreference(somePreference);

Solution 2

Not exactly hiding/showing but if you only want disabling/enabling preference depending on another preference you can specify android:dependency="preferenceKey" or Preference.setDependency(String)

Example from developer.android.com:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="pref_sync"
        android:title="@string/pref_sync"
        android:summary="@string/pref_sync_summ"
        android:defaultValue="true" />
    <ListPreference
        android:dependency="pref_sync"
        android:key="pref_syncConnectionType"
        android:title="@string/pref_syncConnectionType"
        android:dialogTitle="@string/pref_syncConnectionType"
        android:entries="@array/pref_syncConnectionTypes_entries"
        android:entryValues="@array/pref_syncConnectionTypes_values"
        android:defaultValue="@string/pref_syncConnectionTypes_default" />
</PreferenceScreen>

Solution 3

I recommend using V7 preference, it has setVisible() method. But I have not tried it yet.

Solution 4

If you want to implement the hiding of the preference completely in the Preference, here is one example. Does not allow to make it visible again, though.

public class RemovablePreference extends Preference {

@Override
protected void onBindView(View view) {
    super.onBindView(view);

    updateVisibility(); // possibly a better place available?
}

private void updateVisibility() {
    Context context = getContext(); // should be a PreferenceActivity
    if (context instanceof PreferenceActivity) {
        updateVisibility((PreferenceActivity)context);
    }
}

private void updateVisibility(PreferenceActivity activity) {
    updateVisibility(getPreferenceScreen(activity));
}

private PreferenceScreen getPreferenceScreen(PreferenceActivity activity) {

    if (activity.getPreferenceScreen() != null) {
        return activity.getPreferenceScreen(); // for old implementations
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        Fragment fragment = activity.getFragmentManager().findFragmentById(android.R.id.content);
        if (fragment instanceof PreferenceFragment) {
            return ((PreferenceFragment) fragment).getPreferenceScreen();
        }
    }
    return null;
}

private void updateVisibility(PreferenceScreen screen) {
    if (!isVisible() && screen != null) {
        hidePreference(screen, this);
    }
}

private boolean hidePreference(PreferenceGroup prefGroup, Preference removedPreference) {
    boolean removed = false;

    if (prefGroup.removePreference(removedPreference)) {
        removed = true;
    }

    for (int i = 0; i < prefGroup.getPreferenceCount(); i++) {
        Preference preference = prefGroup.getPreference(i);
        if (preference instanceof PreferenceGroup) {
            PreferenceGroup prefGroup2 = (PreferenceGroup)preference;
            if (hidePreference(prefGroup2, this)) {
                // The whole group is now empty -> remove also the group
                if (prefGroup2.getPreferenceCount() == 0) {
                    removed = true;
                    prefGroup.removePreference(prefGroup2);
                }
            }
        }
    }

    return removed;
}

protected boolean isVisible() {
    return true; // override
}
Share:
33,319
Japtar
Author by

Japtar

Updated on October 22, 2020

Comments

  • Japtar
    Japtar over 3 years

    Is there a way to dynamically show and hide preferences? In my case, I have a checkbox preference that would disable or enable one of 2 preference groups ("with-" and "without-handicap" groups). While this would be the ideal GUI in a desktop environment, the "with-handicap" takes up nearly the whole screen, while the other, "without-handicap" takes up only a small portion of the screen.

    Rather than showing both groups at the same time, I'd like to show only one of them at a time, and dynamically show or hide the 2 groups when the checkbox changes. Is there a way to do this?

  • Japtar
    Japtar over 13 years
    Grr, I guess there's no "visible" options in Android, after all. Thanks for the answer.
  • Mickey Tin
    Mickey Tin over 11 years
    After removing some preference second call to 'findPreference()' returns 'null', how to add it again?
  • dhaag23
    dhaag23 about 11 years
    @Mickey Tin: just save the Preference (somePreference) above as a class member when you find it, then you can remove and add it as needed.
  • Bipin Vayalu
    Bipin Vayalu over 10 years
    @dhaag23, It's working fine for main PreferenceScreen but not working for Inner PreferenceScreen, i.e. trying to add PreferenceCategory into inner PreferenceScreen.
  • sfszh
    sfszh over 7 years
    If the preference is nested inside PreferenceCategory, search for PreferenceCategory instead of PreferenceScreen. Then try to referenceCategory.remove/addPreference
  • user149408
    user149408 over 5 years
    @sfszh This. Took me a while to figure that out; I have submitted an edit considering that.
  • user149408
    user149408 over 5 years
    R.layout.hidden appears to be a custom layout. Can you share it? PS: to reverse the operation, you would probably need to figure out the default layout used for preference items and re-assign that.
  • user149408
    user149408 over 5 years
    I just noticed that this causes NPEs when the activity is stopped and restarted (e.g. because it is brought to the background and to the foreground again), as findPreference() will no longer find the preferences which were removed before.
  • Marline
    Marline over 5 years
    @user249408 here is the hidden.xml file <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:visibility="gone"> </LinearLayout>
  • user149408
    user149408 over 5 years
    It works, although you need to do some refactoring (new classes). Also, setting visibility to its present value again reveals some odd bugs (in 23.3.1), so be sure to check that visibility is not yet what you want it to be. Plus, switching from native preferences to preference-v7 results in an outdated LAF for the preferences, haven’t figured out how to fix this.