How to change current Theme at runtime in Android [duplicate]
Asked Answered
D

13

168

I've created a PreferenceActivity that allows the user to choose the theme he wants to apply to the entire application.

When the user selects a theme, this code is executed:

if (...) {
    getApplication().setTheme(R.style.BlackTheme);
} else {
    getApplication().setTheme(R.style.LightTheme);
}

But, even though I've checked with the debugger that the code is being executed, I can't see any change in the user interface.

Themes are defined in res/values/styles.xml, and Eclipse does not show any error.

<resources>
    <style name="LightTheme" parent="@android:style/Theme.Light">
    </style>

    <style name="BlackTheme" parent="@android:style/Theme.Black">
    </style>    
</resources>

Any idea about what could be happening and how to fix it? Should I call setTheme at any special point in the code? My application consists of several Activities if that helps.

Dioptric answered 20/3, 2010 at 11:36 Comment(1)
T
99

I would like to see the method too, where you set once for all your activities. But as far I know you have to set in each activity before showing any views.

For reference check this:

http://www.anddev.org/applying_a_theme_to_your_application-t817.html

Edit (copied from that forum):

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

        // Call setTheme before creation of any(!) View.
         setTheme(android.R.style.Theme_Dark);

        // ...
        setContentView(R.layout.main);
    }


Edit
If you call setTheme after super.onCreate(savedInstanceState); your activity recreated but if you call setTheme before super.onCreate(savedInstanceState); your theme will set and activity does not recreate anymore

  protected void onCreate(Bundle savedInstanceState) {
     setTheme(android.R.style.Theme_Dark);
     super.onCreate(savedInstanceState);


    // ...
    setContentView(R.layout.main);
}
Tolbooth answered 20/3, 2010 at 12:35 Comment(10)
It works, but changes are not applied until you don't change from one Activity to another...Dioptric
Please provide a self-contained answer so that if that link dies people will still have access to your answerTetrachord
@TashPemhiwa and other upvoters of that comment: would it have been hard to edit the answer yourself?Gertie
@Tolbooth doesn't getApplicationContext().setTheme() achieve "the once for all activities"? (didn't test it, but it looks like a good place to start)Gertie
@Gertie If no-one states it, how would the user ever know to do any better?Tetrachord
@TashPemhiwa as far as I know you get a notification of your edited content. You can also add a comment stating that you edited.Gertie
@Gertie Fair point.Tetrachord
what about using setTheme(android.R.style.Theme_Dark); in baseActivity.?Mineral
article link is brokenSuggs
Is there really no other way to change the theme as for each activity separately, as the default theme in the manifest file is set. Maybe in the onCreate() method of the Application class?Dampen
S
70

If you want to change theme of an already existing activity, call recreate() after setTheme().

Note: don't call recreate if you change theme in onCreate(), to avoid infinite loop.

Speedometer answered 16/1, 2013 at 20:28 Comment(4)
This answer would be more useful if it cited some evidence or reasoning to explain why it should be necessary to call recreate(), even though the documentation (developer.android.com/reference/android/content/…) just says to setTheme() before any views are instantiated. Are you saying to call recreate() only if views have already been instantiated?Tupelo
In particular, if you're calling setTheme() in your onCreate(), before calling either super.onCreate() or setContentView(), (1) you'll have to check to guard against an infinite loop where you keep recreating; and (2) what have you gained anyway?Tupelo
This is very useful. I already knew how to set the theme before loading, but changing it immediately is neccessary for a reasonable user experience. recreate is just the thing I needed.Solomonsolon
Doesn't work ...Ferrol
C
25

recreate() (as mentioned by TPReal) will only restart current activity, but the previous activities will still be in back stack and theme will not be applied to them.

So, another solution for this problem is to recreate the task stack completely, like this:

    TaskStackBuilder.create(getActivity())
            .addNextIntent(new Intent(getActivity(), MainActivity.class))
            .addNextIntent(getActivity().getIntent())
            .startActivities();

EDIT:

Just put the code above after you perform changing of theme on the UI or somewhere else. All your activities should have method setTheme() called before onCreate(), probably in some parent activity. It is also a normal approach to store the theme chosen in SharedPreferences, read it and then set using setTheme() method.

Chuff answered 1/3, 2015 at 21:11 Comment(6)
Where would you put this code? If you put it in an Activity.onCreate(), will that lead to an endless loop?Tupelo
Just put this code after you perform changing of theme on the UI or somewhere else. All your activities should have method setTheme() called before onCreate(), probably in some parent activity.Chuff
Thanks for your patience as I try to understand this. By "parent activity" I assume you mean the activity that started (using startActivity()) the activity whose theme I want changed.Tupelo
No, by parent activity I mean some BaseActivity that all other activities in your app can extend.Chuff
OK so I guess this code would be in the constructor of the BaseActivity, so that it happens before the onCreate() of the subclass activities?Tupelo
You just need to call setTheme() in BaseActivity before onCreate(). You don't need to do nothing in subclass activitiesChuff
B
16

i got the same problem but i found the solution.

public class EditTextSmartPhoneActivity extends Activity implements DialogInterface.OnClickListener
{
    public final static int CREATE_DIALOG  = -1;
    public final static int THEME_HOLO_LIGHT  = 0;
    public final static int THEME_BLACK  = 1;

    int position;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        position = getIntent().getIntExtra("position", -1);

        switch(position)
        {
        case CREATE_DIALOG:
            createDialog();
            break;
        case THEME_HOLO_LIGHT:
            setTheme(android.R.style.Theme_Holo_Light);
            break;
        case THEME_BLACK:
            setTheme(android.R.style.Theme_Black);
            break;
        default:
        }

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    private void createDialog()
    {
        /** Options for user to select*/
        String choose[] = {"Theme_Holo_Light","Theme_Black"};

        AlertDialog.Builder b = new AlertDialog.Builder(this);

        /** Setting a title for the window */
        b.setTitle("Choose your Application Theme");

        /** Setting items to the alert dialog */
        b.setSingleChoiceItems(choose, 0, null);

        /** Setting a positive button and its listener */
        b.setPositiveButton("OK",this);

        /** Setting a positive button and its listener */
        b.setNegativeButton("Cancel", null);

        /** Creating the alert dialog window using the builder class */
        AlertDialog d = b.create();

        /** show dialog*/
        d.show();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO Auto-generated method stub
        AlertDialog alert = (AlertDialog)dialog;
        int position = alert.getListView().getCheckedItemPosition();

        finish();
        Intent intent = new Intent(this, EditTextSmartPhoneActivity.class);
        intent.putExtra("position", position);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}
Blade answered 1/1, 2013 at 11:51 Comment(7)
This for when you want to user to select a theme at run time.No need to set in manifestBlade
This works. I coupled it with a sharedpreferences config so the theme change is persistent.Agribusiness
is the call to super.onCreate() after setTheme() also critical to make this work? I was under the impression that one just has to call setTheme() before calling setContentView()Henni
@pandalion98 Would you mind providing some pointers as to how you integrated it with sharedpreferences? Highly appreciated.Daisey
@ErlendK.H. Sure. Save something, maybe an integer, using shared preferences. Then the next time you launch the app, you load the saved value and apply the appropriate theme. In this answer, that is the switch(position) inside onCreate. Basically, it's the exact same thing as this answer, but I also saved a value which I check when the app launches (and apply the necessary theme).Agribusiness
@pandalion98 Thanks a lot for the reply. I'm looking at sharedpreferences now, it's really preferable indeed for storing info persistently.Daisey
@ErlendK.H. Any save/load strategy is fine. At the time, I used shared prefs since that project used it a lot. Some projects have a database for settings, so you can also use that. Even Firebase is fine, if you save settings in the cloud for example.Agribusiness
S
13

We have to set theme before calling 'super.onCreate()' and 'setContentView()' method.

Check out this link for applying new theme to whole application at runtime.

Slight answered 4/10, 2012 at 10:10 Comment(3)
Apparently this means "before calling super.onCreate() and setContentView()." BTW I downvoted this answer earlier because it seemed wrong; now I understand what was meant, and I see value in it (though it wasn't expressed clearly). I can't change my vote, though, unless the answer is edited. If that happens, please notify me so I can change my vote.Tupelo
@Tupelo ha ha ha No issues dear.Slight
I edited it and removed the downvote.Tupelo
T
11

I had a similar problem and I solved in this way..

@Override
public void onCreate(Bundle savedInstanceState) {

    if (getIntent().hasExtra("bundle") && savedInstanceState==null){
        savedInstanceState = getIntent().getExtras().getBundle("bundle");
    }

    //add code for theme

    switch(theme)
    {
    case LIGHT:
        setTheme(R.style.LightTheme);
        break;
    case BLACK:
        setTheme(R.style.BlackTheme);
        break;

    default:
    }
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //code

}

this code is for recreate the Activity saving Bundle and changing the theme. You have to write your own onSaveInstanceState(Bundle outState); From API-11 you can use the method recreate() instead

Bundle temp_bundle = new Bundle();
onSaveInstanceState(temp_bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("bundle", temp_bundle);
startActivity(intent);
finish();
Tav answered 12/6, 2013 at 22:21 Comment(0)
T
6

Instead of

getApplication().setTheme(R.style.BlackTheme);

use

setTheme(R.style.BlackTheme);

My code: in onCreate() method:

super.onCreate(savedInstanceState);

if(someExpression) {
    setTheme(R.style.OneTheme);
} else {
    setTheme(R.style.AnotherTheme);
}

setContentView(R.layout.activity_some_layout);

Somewhere (for example, on a button click):

YourActivity.this.recreate();

You have to recreate activity, otherwise - change won't happen

Technicolor answered 4/12, 2015 at 10:57 Comment(0)
R
3

This is what i have created for Material Design. May it will helpful you.

Have a look for MultipleThemeMaterialDesign

Rotunda answered 20/8, 2015 at 7:24 Comment(0)
I
2

I know that i am late but i would like to post a solution here: Check the full source code here. This is the code i used when changing theme using preferences..

SharedPreferences pref = PreferenceManager
        .getDefaultSharedPreferences(this);
String themeName = pref.getString("prefSyncFrequency3", "Theme1");
if (themeName.equals("Africa")) {
    setTheme(R.style.AppTheme);
} else if (themeName.equals("Colorful Beach")) {
    //Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
    setTheme(R.style.beach);
} else if (themeName.equals("Abstract")) {
    //Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
    setTheme(R.style.abstract2);
} else if (themeName.equals("Default")) {
    setTheme(R.style.defaulttheme);
}
Iconoscope answered 24/10, 2015 at 5:21 Comment(0)
A
2

This way work for me:

  @Override
protected void onCreate(Bundle savedInstanceState) {
    setTheme(GApplication.getInstance().getTheme());
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);
}

Then you want to change a new theme:

GApplication.getInstance().setTheme(R.style.LightTheme);
recreate();
Anile answered 15/11, 2016 at 10:7 Comment(0)
L
1

You can finish the Acivity and recreate it afterwards in this way your activity will be created again and all the views will be created with the new theme.

Liquorish answered 18/7, 2012 at 9:34 Comment(0)
B
0

Call SetContentView(Resource.Layout.Main) after setTheme().

Beanfeast answered 7/6, 2016 at 4:52 Comment(0)
S
0

This had no effect for me:

public void changeTheme(int newTheme) {
    setTheme(newTheme);
    recreate();
}

But this worked:

int theme = R.style.default;

protected void onCreate(Bundle savedInstanceState) {
    setTheme(this.theme);
    super.onCreate(savedInstanceState);
}

public void changeTheme(int newTheme) {
    this.theme = newTheme;
    recreate();
}
Spano answered 22/6, 2016 at 17:18 Comment(2)
According to your code, you set the theme to a variable that is never used. I believe the setTheme within onCreate should be setTheme(theme);Bodhisattva
The Activity will be destroyed and recreated when calling recreate(), so the variable is never be used. Just adding static before the defined to make it work.Cipher

© 2022 - 2024 — McMap. All rights reserved.