Search code examples
androidpreferencefragmentandroid-night-modeandroid-dark-theme

setDefaultNightMode() only works after second call in PreferenceFragment


I am working to implement a dark mode into my app. Right now I try to switch the UI mode between light and dark using a PreferenceFragment nested inside an AppCompatActivity. The App behaves like this, starting from the light theme as a default value:

  • If I select 'dark' in the ListPreference, the summary of the preference changes, but the UI stays light in this and all the other activities.
  • If I select 'dark' a second time, the activity switches to dark theme, as well as the other activities in the backstack.
  • The same thing happens in reverse, when I want to switch back to the light theme.

So basically everything works, but you have to select the desired value in the ListPreference twice. The code for the ListPreference:

final Preference listPreferenceDesign = findPreference(PREF_DESIGN);
listPreferenceDesign.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object o) {
            if (((ListPreference) preference).getValue().equals("light")) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            } else if (((ListPreference) preference).getValue().equals("dark")) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            }
            return true;
    }
});

What I tried so far:

  • Call getActivity.recreate()before the return true; statement
  • Call getActivity.recreate()after a short delay using a Handler
  • Call this.recreate() in the onResume() method of the parent Activity when a boolean changedDesignSetting was true

I am thankful for further help.


Solution

  • I finally get it to work using an OnSharedPreferenceChangeListener in the parent activity. Now the code in the parent activity goes like this:

    getFragmentManager().beginTransaction().replace(R.id.settingsPlaceholderID, preferenceFragment).commit();
    SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            if (key.equals(PREF_DESIGN)) {
                if (sharedPreferences.getString(key, "light").equals("light")) {
                    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
                } else {
                    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
                }
            }
        }
    };
    
    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
    sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
    

    And in the PreferenceFragment I only have:

    final Preference listPreferenceDesign = findPreference(PREF_DESIGN);
    listPreferenceDesign.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference, Object o) {           
            return true;
        }
    });