3

I have an application where the user can chose between several different colored themes from a PreferenceActivity and thereby change the theme / color of the entire application. But the changes selected in the PreferenceActivity do not apply immediately. The changes are applied only when the user reenters the PreferenceActivity.

I know I can call recreate() every time a theme is chosen, but I want to know if a better solution exists without recreating the entire activity.

Here is an video of how it currently works: https://www.youtube.com/watch?v=oU8xIUi_48A

This is where I set the chosen value from the preferenceList in my PreferenceActivity:

@Override
public void onCreate(Bundle savedInstanceState) {
    setTheme();
    themecolorList.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            switch (themecolorList.getValue()) {

                case "grey":
                    themecolorList.getEditor().putString("grey", "green").apply();
                    break;
                case "green":
                    themecolorList.getEditor().putString("green", "green").apply();
                    setTheme(R.style.AppTheme_default);
                    break;
                case "blue":
                    themecolorList.getEditor().putString("blue", "green").apply();
                    break;
                case "yellow":
                    themecolorList.getEditor().putString("yellow", "green").apply();
                    break;
                case "red":
                    themecolorList.getEditor().putString("red", "green").apply();
                    break;
                case "pink":
                    themecolorList.getEditor().putString("pink", "green").apply();
                    break;

                default:
                    themecolorList.getEditor().putString("green", "green").apply();
                    break;
            }

            recreate();
            return true;


        }
    });
  }

The method setTheme(); is called in my PreferenceActivitys onCreate(); method

private void setTheme() {

   SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
    switch (sharedPreferences.getString("THEME_KEY", "green")) {

        case "grey":
            setTheme(R.style.AppTheme_Grey);
            break;

        case "green":
            setTheme(R.style.AppTheme_default);
            break;

        case "blue":
            setTheme(R.style.AppTheme_Blue);
            break;

        case "yellow":
            setTheme(R.style.AppTheme_Yellow);
            break;

        case "red":
            setTheme(R.style.AppTheme_Red);
            break;

        case "pink":
            setTheme(R.style.AppTheme_Pink);
            break;

        default:
            getApplication().setTheme(R.style.AppTheme_default);
            setTheme(R.style.AppTheme_default);
            break;
    }
}
NuSkooler
  • 5,391
  • 1
  • 34
  • 58

5 Answers5

8

It seems to be that the best solution is to use recreate(); since there is no other way to refresh a whole layout for an activity:

For everytime the user presses on a options from the list of themes the key/value for the pressed one is saved via OnPreferenceChangeListener in a SharedPreference and recreate(); is then called.

themecolorList.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            recreate();
            return true;
        }
    });

In my PreferenceActivitys onCreate(); I call a custom made method setTheme(); which is called after recreate(); is called. The setTheme(); just looks up what is saved in SharedPreference from the OnPreferenceChangeListener and set the theme to corresponding value

 @Override
 public void onCreate(Bundle savedInstanceState) {
    setTheme();
    super.onCreate(savedInstanceState);
}
rekire
  • 47,260
  • 30
  • 167
  • 264
0

You can't. You can start a new activity for selecting theme and when user selects finish that activity.

Eren Utku
  • 1,731
  • 1
  • 18
  • 27
  • Using `recreate();` will be a better option then, than starting a new intent for everytime the user presses on an option in the preferencelist –  Jun 15 '16 at 00:03
0

Triggering a layout update (by rotating the device, or making the app think it's rotated) should normally cause it to reload its resources.

I don't remember exactly how to do it at the moment, but it was a common practice the last time I investigated how to do something similar. Just remember to specify (in the manifest?) that your activity will handle layout changes/screen rotation to avoid a full restart (and loss of any unsaved data).

ToVine
  • 560
  • 5
  • 16
  • Sounds as a cool solution. The only catch is that I have made my App to only work in potrait mode, since using it in landscape mode is unpractical. –  Jun 15 '16 at 00:04
  • Haha, I had the exact same problem as you then! :p (http://stackoverflow.com/questions/11705963/forcing-a-different-locale-works-only-for-top-activity-in-back-stack/11710007#comment15537029_11710007). But then I think you're stuck with recreating the activity, alternatively you could make a preview (image popup, or apply style colours to the list item, if possible?) and then just recreate the activity (using a Bundle to supply the current state to the new instance if needed) when the user makes a selection. – ToVine Jun 15 '16 at 07:00
0

You can use this. It works for me.

@HiltAndroidApp
class App : Application(){

    companion object{
       private var isOpening = true
        fun appIsOpening(sync : (Boolean) -> Unit){
            sync(isOpening)
            isOpening = false
        }
    }

}

and

@AndroidEntryPoint
class SingleActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        installSplashScreen()
        setContentView(R.layout.activity_main)

        App.appIsOpening { it ->
            if (it)setStartDestination()
        }
    }


}
-1

It looks like there is a method called invalidate() to be called on views that need to be redrawn.

Link

Which should allow you to call on individual views that only need redrawing. Not the whole activity.