ilomambo
ilomambo

Reputation: 8350

Android A way to trigger onSharedPreferenceChange() for a/all preference

  1. I load a new set of settings by reading a text file and parsing it.
  2. For each setting I found there I extract the value and update the corresponding preference (whether it is CheckPreference, ListPreference or EditTextPreference).
  3. All this is done from the settings fragment. Meaning the preference screen is in view while I make the changes.
  4. Because I change the vaules programatically (editor().commit), onSharedPreferenceChange() is not triggered.

That's why I refresh all preferences afterwards with this code:

private void refreshPreferences() {
    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
    for(int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
        onSharedPreferenceChanged(sp, getPreferenceScreen().getPreference(i).getKey());
    }
}

It works perfectly. I just wonder if a system call to invalidate all preferences exists that would achieve the same result (an automatic call to onSharedPreferenceChanged() )

I am not anal, I am just not fond of manually calling methods that are supposed to be called by the OS.

Edit
This is my current code that changes a preference. I tried before to change with editor but was also not generating onSharePreferncesChanged() calls.
source: it's a multiline string that was read from a file

private enum Type { INT, BOOLEAN, EDITTEXT, LIST }; 

private void loadPreference(int key, char [] source, Type t) {
    String s = new String(source);
    Log.i(TAG, "+ loadPreference(key:" + key + ", source:(" + s + "), t:" + t + ")");
    String sKey = getText(key).toString();
    Matcher m;
    Preference p = findPreference(sKey);
    switch( t ) {
    case BOOLEAN:
        m = Pattern.compile("(?m)^" + sKey + "=\"(true|false)\"$").matcher(s);
        if( m.find() ) ((CheckBoxPreference)p).setChecked(m.group(1).equals("true"));
        break;
    case INT:
        m = Pattern.compile("(?m)^" + sKey + "=\"(\\d+)\"$").matcher(s);
        if( m.find() ) ((EditTextPreference)p).setText(m.group(1));
        break;
    case EDITTEXT:
        m = Pattern.compile("(?m)^" + sKey + "=\"(.*)\"$").matcher(s);
        if( m.find() ) ((EditTextPreference)p).setText(m.group(1));
        break;
    case LIST:
        m = Pattern.compile("(?m)^" + sKey + "=\"(\\d+)\"$").matcher(s);
        if( m.find() ) ((ListPreference)p).setValueIndex(Integer.valueOf(m.group(1)));
        break;
    default:
    }
    Log.i(TAG, "- loadPreference()");
}

public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    Log.i(TAG, "+ onSharedPreferenceChanged(prefs:" + prefs + ", key:" + key + ")");
    if( key != null ) {
        updatePreference(prefs, key);
        updateSummary(findPreference(key));
    } else {
        Log.e(TAG, "Preference without key!");
    }
    Log.i(TAG, "- onSharedPreferenceChanged()");
}

@Override
public void onResume() {
    Log.i(TAG, "+ onResume()");
    super.onResume();
    // Set up a listener
    getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    Log.i(TAG, "- onResume()");
}

@Override
public void onPause() {
    Log.i(TAG, "+ onPause()");
    super.onPause();
    // Unregister the listener
    getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    Log.i(TAG, "- onPause()");
}

Upvotes: 5

Views: 5638

Answers (2)

Evgueni Stoyanov
Evgueni Stoyanov

Reputation: 11

OnSharedPreferencesChangeListener is not triggered when the new value is equal to the previous value. In this case SharedPreferences is not updated.

I worked around this clearing the old value when appropriate.

Upvotes: 1

saschoar
saschoar

Reputation: 8230

The guideline-compliant way to achieve this is to use an OnSharedPreferenceChangeListener.

In point 4 of your question, you say

Because I change the vaules programatically, onSharedPreferenceChange() is not triggered.

This depends on how you change the settings. In your point 2, you say

For each setting I found there I extract the value and update the corresponding preference...

Please make sure that you use the settings editor and commit your changes, similar to this:

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key","value"); // your updated setting
editor.commit();

If your other class properly implements an OnSharedPreferenceChangeListener and has registered it by calling registerOnSharedPreferenceChangeListener(this) on your default SharedPreferences, you should be able to listen for these changes in the onSharedPreferenceChanged() method.

At least that's what the article in the Dev Guide on Settings suggests and I have implemented it successfully in a similar way ("programmatically" changing settings).

Updated answer:
Since your comment says the above doesn't work, I had a look in my code again. Indeed, I got your question wrong by thinking you want to achieve this from a different class.

If you are in a PreferenceFragment (or PreferenceActivity), you can also implement the OnSharedPreferenceChangeListener interface, but register it for the preferences displayed in your current PreferenceScreen. Best practice would be to register it in onResume() by calling

getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);

and then unregister in onPause():

getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);

As described previously, you receive the preference changes in onSharedPreferenceChanged() of your PreferenceFragment then. This is (now for real) the way it works in my code.

Upvotes: 4

Related Questions