Android PreferenceScreen - Can I use it without saving preferences to SharedPreferences?
Asked Answered
S

5

17

I'm trying to use the Android PreferenceScreen as a convenient way to layout my user settings, but I don't want to save the preferences to the device shared preferences. Is this possible, or should I be using a different mechanism such as ListView?

It seems really convenient to use the PreferenceScreen type because I require different widgets (ie. switches, edit text). But I'm already running into problems with persistence, ie. anything I enter is persisted even across sessions when I don't want it to be.

settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Preference
        android:key="settings_type"
        android:title="@string/label_type" />

    <EditTextPreference
        android:key="settings_edit_name"
        android:title="@string/label_name"
        android:dialogTitle="Enter a name"
        android:singleLine="true" />

    <SwitchPreference
        android:key="settings_edit_state"
        android:title="@string/label_state"
        android:summary="Enable or disable the state" />

</PreferenceScreen>
Stately answered 31/1, 2013 at 19:12 Comment(0)
M
1

You can just use a linear layout and place the non-preference versions of the components you want in it.

Mountaineer answered 31/1, 2013 at 19:22 Comment(7)
I'm trying to take advantage of the PreferenceScreen because it's a list that allows me to use different widgets within it. In my example, I don't want any preferences saved.Stately
Right that is the whole point of the preference widgets, you really just need regular widgets like EditText and Switch, every preference widget has a regular version too. Just place these in what ever layout you want and then add the listeners you need for them.Mountaineer
I agree, it's not a good idea to try to override the preference behaviour.Stately
What is the non-preference versions of the components? For example, what should I use instead for EditTextPreference? I tried EditText, but it can not set summary and display pop-up dialog when I click on it.Galliett
@Galliett Had they used ids instead of keys, all of this could be avoided, Google being Google. You'll need to do it all manually, but it's easy. Put two TextViews vertically inside a LinearLayout, one is the title, the other the summary. Then, set an OnClickListener on the layout that opens an AlertDialog/DialogFragment (depending on what you want) with the title, EditText and accept/cancel buttons. Style everything as you wish, or you could create custom views that simulate preference widgets.Judd
@Galliett if you open any preference widget xml layout, you'll see it's internally a bunch of layouts with normal widgets (textview, imageview, etc). You could in fact just copy it.Judd
@Galliett There are no alternatives to preference componentsLegionnaire
P
21

I know that's an old question but I find it quite interesting. Just set this attribute to your preference and it won't be saved:

android:persistent="false"
Perorate answered 23/9, 2013 at 8:36 Comment(2)
The preference listeners don't seem to get any notification at all when this is set.Millais
Wonderful, this is the key to separate the view from the logic! Together with Preference.setOnPreferenceChangeListener() it allows for alternative storage locations and can be used with MVVM like systems.Nonconformity
S
6

I re-read the appropriate docs and determined how to prevent saving preferences by implementing the Preference.OnPreferenceChangeListener interface.

public static interface Preference.OnPreferenceChangeListener

Interface definition for a callback to be invoked when the value of this Preference has been changed by the user and is about to be set and/or persisted. This gives the client a chance to prevent setting and/or persisting the value.

Example:

public class SettingsFragment extends PreferenceFragment {

    public static SettingsFragment newInstance(int index) {
        SettingsFragment f = new SettingsFragment();

        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);

        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.layout.settings);

        // getArguments().getInt("index");

        EditTextPreference namePreference = (EditTextPreference) findPreference("settings_edit_name");
        namePreference.setOnPreferenceChangeListener(new NamePreferenceChangeListener());

    }

    private class NamePreferenceChangeListener implements Preference.OnPreferenceChangeListener {

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        // Do something else presumably and then return false to avoid saving the pref.
        return false;
    }

}
Stately answered 31/1, 2013 at 20:11 Comment(1)
When returning false from that method, it seems like the state (of for example an CheckBoxPreference) doesn't change at all, making it quite hard to do anything.Manchineel
M
1

You can just use a linear layout and place the non-preference versions of the components you want in it.

Mountaineer answered 31/1, 2013 at 19:22 Comment(7)
I'm trying to take advantage of the PreferenceScreen because it's a list that allows me to use different widgets within it. In my example, I don't want any preferences saved.Stately
Right that is the whole point of the preference widgets, you really just need regular widgets like EditText and Switch, every preference widget has a regular version too. Just place these in what ever layout you want and then add the listeners you need for them.Mountaineer
I agree, it's not a good idea to try to override the preference behaviour.Stately
What is the non-preference versions of the components? For example, what should I use instead for EditTextPreference? I tried EditText, but it can not set summary and display pop-up dialog when I click on it.Galliett
@Galliett Had they used ids instead of keys, all of this could be avoided, Google being Google. You'll need to do it all manually, but it's easy. Put two TextViews vertically inside a LinearLayout, one is the title, the other the summary. Then, set an OnClickListener on the layout that opens an AlertDialog/DialogFragment (depending on what you want) with the title, EditText and accept/cancel buttons. Style everything as you wish, or you could create custom views that simulate preference widgets.Judd
@Galliett if you open any preference widget xml layout, you'll see it's internally a bunch of layouts with normal widgets (textview, imageview, etc). You could in fact just copy it.Judd
@Galliett There are no alternatives to preference componentsLegionnaire
E
0

Since the preferences are bound to be stored, you can always clear them in onCreate

Try this in onCreate of PreferenceFragment

super.onCreate();
getPreferenceManager().setSharedPreferencesName("custom");
SharedPreferences sp = getPreferenceManager().getSharedPreferences();
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.apply();
Edmanda answered 31/1, 2013 at 19:41 Comment(1)
If you save the preferences just to erase them later, I feel that draksia's solution is much better.Manchineel
L
0

I recommend creating a subclass of PreferenceDataStore and assigning it to your PreferenceManager.

Preference Data Store

A data store interface to be implemented and provided to the Preference framework. This can be used to replace the default android.content.SharedPreferences, if needed.

PreferenceManager

public void setPreferenceDataStore (PreferenceDataStore dataStore)

Sets a PreferenceDataStore to be used by all preferences associated with this manager that don't have a custom PreferenceDataStore assigned via Preference.setPreferenceDataStore(PreferenceDataStore). Also if the data store is set, the child preferences won't use SharedPreferences as long as they are assigned to this manager.

Example

CustomDataStore

    class CustomDataStore: PreferenceDataStore() {
    override fun putBoolean(key: String?, value: Boolean) {
        when (key) {
            knownKey -> // Do something
            anotherKnownKey -> // Do somthing else
        }
    }

    override fun putString(key: String?, value: String?) {
        when (key) {
            knownKey -> // Do something
            anotherKnownKey -> // Do something else
        }
    }

    // You can ignore unused types but make sure to still override to avoid UnsupportedOperationException
    override fun putFloat(key: String?, value: Float) {}
    override fun putInt(key: String?, value: Int) {}
    override fun putLong(key: String?, value: Long) {}
    override fun putStringSet(key: String?, values: MutableSet<String>?) {}

    override fun getBoolean(key: String?, defValue: Boolean): Boolean {
        return when (key) {
            knownKey -> // Do something
            anotherKnownKey -> // Do something else
            else -> defValue
        }
    }

    override fun getString(key: String?, defValue: String?): String? {
        return when (key) {
            knownKey -> // Do something
            anotherKnownKey -> // Do something else
            else -> defValue
        }
    }
}

NOTE: Be careful while subclassing, because all the abstract put() methods will throw an UnsupportedOperationException.

PreferenceFragment or PreferenceFragmentCompat

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
    // Prevent saving to SharedPreferences, use a custom data store
    preferenceManager.preferenceDataStore = CustomDataStore()
}
Lonnielonny answered 24/11, 2020 at 3:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.