35

Based on the following code, can you tell me how to refresh the PreferenceActivity window to show changes in the settings immediately? For example: the user taps the master chime toggle checkbox to true (ticked), I would like the user to immediately see the other settings such as the ChimeOn15Past checkbox also be be true (ticked)

SharedPreferences.Editor prefEditor = clockSettings.edit(); // Allow the settings to be changed.

if (booleanMasterChimeToggle == true) {
    prefEditor.putBoolean("ChimeOnTheHour", true);
    prefEditor.putBoolean("ChimeOn15Past", true);
    prefEditor.putBoolean("ChimeOn30Past", true);
    prefEditor.putBoolean("ChimeOn45Past", true);

    strNotifyMessage = "Full chiming has now been set.";

} else {
    prefEditor.putBoolean("ChimeOnTheHour", false);
    prefEditor.putBoolean("ChimeOn15Past", false);
    prefEditor.putBoolean("ChimeOn30Past", false);
    prefEditor.putBoolean("ChimeOn45Past", false);

    strNotifyMessage = "Full chiming has now been disabled.";
}
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Emad-ud-deen
  • 4,734
  • 21
  • 87
  • 152

10 Answers10

46

Instead of

finish();
startActivity(getIntent());

I prefer the following code :

setPreferenceScreen(null);
addPreferencesFromResource(R.xml.preferences);

This will reset the display as well but without the whole finish procedure and its consequences. Plus this code works well with PreferenceActivity and PreferenceFragment.

This is interesting if you would like to change dynamically the locale value for example. In this case, working with the manager is not enough because you want a full reload of titles and values.

EDIT: setPreferenceScreen is deprecated in PreferenceActivity (still working) but is not in PreferenceFragment

Solostaran14
  • 1,656
  • 20
  • 22
  • I tried that with an example of mine : changing the locale within the preference screen. And it works well. I didn't see side effects ... – Solostaran14 Sep 04 '13 at 18:38
  • This is exactly what I needed for a different but similar problem. I wanted to make sure an embedded `PreferenceFragment` would update if the user changed a `preference` in `PreferenceActivity` then navigated back. Just used your code in `onResume`. – theblang Aug 27 '14 at 16:34
  • 1
    This helped me with my problem where I need to refresh the preference screen after in-app purchase is successful... thanks! – Bruce Oct 05 '14 at 15:09
  • @DerGolem the method is not deprecated in `PreferencesFragment`: http://developer.android.com/reference/android/preference/PreferenceFragment.html#setPreferenceScreen(android.preference.PreferenceScreen) – Denys Kniazhev-Support Ukraine Jan 07 '15 at 15:42
  • 3
    @Mr_and_Mrs_D, yes; the screen is reset, so if you're scrolled part-way through it, this will make it scroll back to the top when you change a preference. – Sam Feb 11 '15 at 20:29
  • Love it! Don't forget to clear all your PreferenceChangeListeners via `getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);` before you refresh to avoid unnecessary shots in the dark and possible exceptions. You can call `getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);` immediately afterwards to re-register – avalancha Nov 03 '15 at 11:58
  • 1
    You could just bind call bindPreferenceSummaryToValue() on the specific preference items again when you know they will need to be refreshed. This entire line of thought seems like all sorts of overkill to me though. – JoeHz Dec 22 '16 at 09:26
38

Nikolay's answer is correct. I just want to add some code here to illustrate his point more clearly.

private CheckBoxPreference mOn15Past;
private CheckBoxPreference mOn30Past;
private CheckBoxPreference mOn45Past;

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

    // Load the preferences from an XML resource
    addPreferencesFromResource(R.xml.preferences);

    mOn15Past = (CheckBoxPreference) findPreference("ChimeOn15Past");
    mOn30Past = (CheckBoxPreference) findPreference("ChimeOn30Past");
    mOn45Past = (CheckBoxPreference) findPreference("ChimeOn45Past");

    final Preference chimeMaster = findPreference("ChimeMaster");
    chimeMaster.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference, Object newVal) {
            final boolean value = (Boolean) newVal;
            mOn15Past.setChecked(value);
            mOn30Past.setChecked(value);
            mOn45Past.setChecked(value);
            return true;
        }

    });
}

In short, PreferenceActivity is not designed to refresh its values from the persistent storage once it is started. Instead of using SharedPreferences.Editor to modify and commit additional changes like you did in your code, it is better to make the changes into the local PreferenceManager object which will be committed by the PreferenceActivity in its normal lifecycle.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Joe
  • 14,039
  • 2
  • 39
  • 49
  • 2
    This is the "right" way to do it. Let the PreferenceScreen manage its own refresh instead of fighting with SharedPreferences & manually refreshing the UI. – pmont Aug 08 '13 at 08:00
16

There's a simple method to refresh all items on the list at once. Simply do the following:

getPreferenceScreen().removeAll();
addPreferencesFromResource(R.xml.preferences);

With this approach you won't loose ListView's position.

4

You can simply call onCreate method. If you want you can call onStart and onResume methods.

onCreate(null);

I have solved my problem with this way.

Arda Ç.
  • 504
  • 5
  • 7
3

Implement onPreferenceChange and toggle related perfs from there. To 'refresh' the whole activity, you need to finish() and restart it.

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • Thanks for the reply. Can I do this from a widget where I can call the PreferecActivity method "RefreshMe" ? If the answer is yes, can you show me the needed code to perform this? Thanks. – Emad-ud-deen Nov 04 '11 at 09:30
  • Not really. You can only start an Activity from your widget, you need to do your processing from the Activity. Why do you want to do this from a widget? – Nikolay Elenkov Nov 04 '11 at 13:31
  • Because the user may have previously opened my app settings screen then pressed the home button to quickly get back onto the home screen. Later in the day that user may use the widget to turn off the master chime on / off toggle and then if the user taps the app icon to call the app, the preference screen with be the last screen the user was looking at. I was hoping that screen would reflect the change the user made in the master chime on / off toggle from the widget. – Emad-ud-deen Nov 04 '11 at 13:44
  • Preferences are loaded from disk, so your preference activity should reflect the changes, if saved correctly. – Nikolay Elenkov Nov 04 '11 at 13:47
  • The preferences are saved with a PreferenceEditor in the AppWidgetProvider. Currently the only way for the user to see the changes in the PreferenceActivity is if they use the device back button and call the PreferenceActivity again. Is there a way to refresh the screen from the AppWidgetProvider? – Emad-ud-deen Nov 04 '11 at 13:54
  • Edit your question and explain in detail what you are trying to do and what the structure of your app is. – Nikolay Elenkov Nov 04 '11 at 14:10
2

After fighting this for a day, I figured out this.
This is for multiple levels of preferences.

parent is the preference screen that holds the preference you are trying to update.

Since the settings screen displays as a dialog, you can get the listview from the dialog, then tell it's adapter to update child.

PreferenceScreen parent  // The screen holding the child
PreferenceScreen child   // The entry changing

child.setSummary(isEnabled?"Enabled":"Not Enabled");
ListView v = (ListView)parent.getDialog().findViewById(android.R.id.list);
BaseAdapter ba = (BaseAdapter)v.getAdapter();
ba.notifyDataSetChanged();

I found R.id.list is not available on older phones but this works

    ListAdapter adapter = parent.getRootAdapter();
    if (adapter instanceof BaseAdapter) {
        ((BaseAdapter)adapter).notifyDataSetChanged();
    }
1

This class indicate simple preference screen. Which may help you.

public class PreferenceActivitySettings extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
        private SharedPreferences preferences;
        @SuppressWarnings("deprecation")
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.settings);
            preferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
            preferences.registerOnSharedPreferenceChangeListener(this);
        }

        @SuppressWarnings("deprecation")
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            setPreferenceScreen(null);
            addPreferencesFromResource(R.xml.settings);
        }
    }
Vinil Chandran
  • 1,563
  • 1
  • 19
  • 33
0

I managed to write a method that's actually quite simple and without need for invalidating ListViews or or whatever the situation may be. This method makes a call to the public ViewGroup removeView(View view), View requestLayout(), View forceLayout(), ViewGroup addView(View view), and finally a call to Fragment OnCreate(Bundle savedInstanceState) methods with a few null checks and a final View temporary variable to store the actual view when the view is removed.

This method is still quite new and I'll be giving it some tweaking and refining over the next 2 days but it works flawlessly as is with no whack side effects. Since the class I've written holding the code changes the actual preference title and summary text styles for the whole rom (that was the whole point to the options I've added), I've also added a public constructor the classes that are activities that lie in the activity stack below the preference style screen so they can be updated at the same time. Most likely I will be moving this new method into the frameworks view class so it can be made available system wide.

public void reLoadView(View view) {
    if (view == null) {
        view = mView;
        Log.i(TAG, "The current View is: " + view);
    }
    final View tmpView = view;
    try {
        setStyleChanged(1);
        setReset(0);
        Log.i(TAG, "The Temporary View is: " + tmpView);
        Log.i(TAG, "The current View is: " + mView);
        Log.i(TAG, "The current ViewGroup is: " + mViewGroup);
        if (mView != null) {
            if (mViewGroup != null) {
                mActivity.runOnUiThread(new Runnable() {
                    @Override 
                    public void run() {
                        mViewGroup.removeView(mView);
                        mView.requestLayout();
                        mView.forceLayout();
                        mViewGroup.addView(tmpView);
                        onCreate(new Bundle());
                    } 
                });
            }
        }
        View tmp = null;
        mDemented.reLoadView(tmp);
    } catch (Exception e) {
    }
}

The view variables themselves are set initially in the class initialization and are initialized and defined in the View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) and the View onViewCreated(View view, Bundle savedInstanceState). The view variables themselves are set initially in the class initialization.

private View mView;
private ViewGroup mViewGroup;

private int mLayoutResId = R.layout.preference_list_fragment;//holds the layout reference just to keep it clean
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
    View rootView = inflater.inflate(mLayoutResId, parent, false);
    mViewGroup = parent;
    return rootView;
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    mView = view;
}

In the activities in the stack below, my PreferenceStyle class, I set an empty constructor which can be initialized anywhere in the app.

public class DEMENTED extends SettingsPreferenceFragment implements
        Preference.OnPreferenceChangeListener, View.OnClickListener {

    public DEMENTED() {
         super();
    }

    //other code to do whatever here

In my PreferenceStyle class I imported my DEMENTED class then set it as a variable prior to onCreate(Bundle savedInstanceState):

private DEMENTED mDemented;

Then initialized the variable in onCreate(Bundle savedInstanceState):

mDemented = new DEMENTED();

The call to my DEMENTED class to reload its view is done in my reloadView(View view) method but the variable view used to make the call is a View variable set immediately prior to the call and is set as null:

View tmp = null;
mDemented.reLoadView(tmp);

and my DEMENTED class checks to see if the included View in the method call is null and if so sets it to a localized View variable so the method can do its work with local variables:

public void reLoadView(View view) {
    if (view == null) {
        view = mView;
        Log.i(TAG, "The current View is: " + view);
    }
    //all the other good stuff here

Basically, this uses the ViewGroup as defined in onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState):

    mViewGroup = parent;

and View variable is set in onViewCreated(View view, Bundle savedInstanceState)

    mView = view;

Hope this helps someone out because this has been a question I've seen repeated on here over and over and I have yet to find a solid solution basically anywhere that didn't involve multiple classes used as utils. If anyone has any comment suggestions to simplify or whatever feel free to comment.

Laurel
  • 5,965
  • 14
  • 31
  • 57
cphelps76
  • 75
  • 1
  • 2
  • 10
0

After you commit the changes to the sharedPreferences, I think you should just call invalidateHeaders() . Docs about it:

Call when you need to change the headers being displayed. Will result in onBuildHeaders() later being called to retrieve the new list.

This should refresh the UI, to show the new values.

android developer
  • 114,585
  • 152
  • 739
  • 1,270
-1

If you're targeting API 11 and above, you can use invalidateHeaders.

From the docs:

Call when you need to change the headers being displayed. Will result in onBuildHeaders() later being called to retrieve the new list.

marmor
  • 27,641
  • 11
  • 107
  • 150
  • Didn't change the setting values shown in the activity in Android 4.3 for me. – Sam Feb 11 '15 at 20:36
  • check your onBuildHeaders() method, that's where you should put your headers initialisation code, and then invalidateHeaders should work – marmor Feb 12 '15 at 08:49