This is how I've done it for my app. I'm sure my approach can help.
Set up your Light and Dark themes in styles.xml like this:
<!-- Use this theme in Manifest by default -->
<style name="MyLightTheme" parent="Base.AppTheme.Light"></style>
<!-- Base light theme containing all styles -->
<style name="Base.AppTheme.Light" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
... Other styles
</style>
<!-- Use this to switch to Dark theme -->
<style name="MyDarkTheme" parent="Base.AppTheme.Dark"></style>
<!-- Base dark theme containing all styles -->
<style name="Base.AppTheme.Dark" parent="Theme.AppCompat">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
... Other styles
</style>
Since you're controlling Theme change via a preference, register a preference change listener in your PreferenceFragment.
PreferenceManager.getDefaultSharedPreferences(getActivity()).registerOnSharedPreferenceChangeListener(this);
Implement onSharedPreferenceChanged():
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.pref_key_nighttheme))) {
if (sharedPreferences.getBoolean(getString(R.string.pref_key_nighttheme), false)) {
// Night theme enabled
getActivity().setTheme(R.style.MyDarkTheme);
getActivity().getApplication().setTheme(R.style.MyDarkTheme);
darkTheme = true;
} else {
getActivity().setTheme(R.style.MyLightTheme);
getActivity().getApplication().setTheme(R.style.MyLightTheme);
darkTheme = false;
}
getActivity().recreate(); // This is important. It allows the theme change to take effect.
}
}
Be sure to recreate your MainActivity in onResume() if Back Navigation leads to MainActivity.
Additionally, you must check for the current theme in EVERY Activity, BEFORE super() is called in onCreate().
isThemeDark = setDarkTheme(this);
setDarkTheme() is a helper I've created which checks the current Theme via SharedPreference. It checks if a Theme change is necessary or not.
public static boolean setDarkTheme(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean isDarkTheme = prefs.getBoolean(context.getString(R.string.pref_key_nighttheme), false);
context.setTheme(SettingsActivity.darkTheme ? R.style.MyDarkTheme : R.style.MyLightTheme);
return isDarkTheme;
}
Here's how Night mode works in my app Newslet:

UPDATE:
Android now officially supports Night Mode via its AppCompat DayNight Theme.
Here's a tutorial on the same.