167

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.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Guido
  • 46,642
  • 28
  • 120
  • 174

13 Answers13

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);
}
Mehdi Yari
  • 481
  • 3
  • 12
Pentium10
  • 204,586
  • 122
  • 423
  • 502
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.

TPReal
  • 1,539
  • 12
  • 26
  • 3
    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 (http://developer.android.com/reference/android/content/Context.html#setTheme(int)) just says to `setTheme()` before any views are instantiated. Are you saying to call recreate() only if views have already been instantiated? – LarsH Aug 17 '15 at 16:07
  • 3
    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? – LarsH Aug 17 '15 at 18:21
  • 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. – Kjeld Schmidt Mar 04 '19 at 13:39
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.

Community
  • 1
  • 1
yyunikov
  • 5,719
  • 2
  • 43
  • 78
  • Where would you put this code? If you put it in an Activity.onCreate(), will that lead to an endless loop? – LarsH Aug 17 '15 at 19:55
  • 1
    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. – yyunikov Aug 17 '15 at 20:33
  • 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. – LarsH Aug 17 '15 at 20:55
  • No, by parent activity I mean some `BaseActivity` that all other activities in your app can extend. – yyunikov Aug 18 '15 at 07:31
  • 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? – LarsH Aug 18 '15 at 15:11
  • You just need to call `setTheme()` in `BaseActivity` before `onCreate()`. You don't need to do nothing in subclass activities – yyunikov Aug 18 '15 at 15:17
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);
    }
}
Monty
  • 3,205
  • 8
  • 36
  • 61
  • 3
    This for when you want to user to select a theme at run time.No need to set in manifest – Monty Jan 01 '13 at 11:52
  • This works. I coupled it with a sharedpreferences config so the theme change is persistent. – Aloha Jul 14 '15 at 07:44
  • 2
    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()` – Someone Somewhere Apr 08 '16 at 13:28
  • @pandalion98 Would you mind providing some pointers as to how you integrated it with sharedpreferences? Highly appreciated. – Erlend K.H. Jan 06 '19 at 13:51
  • 1
    @ErlendK.H. Sure. Save something, [maybe an integer](https://stackoverflow.com/q/16194567/3979290), using shared preferences. Then the next time you launch the app, you [load](https://stackoverflow.com/a/16194592/3979290) 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). – Aloha Jan 06 '19 at 14:38
  • @pandalion98 Thanks a lot for the reply. I'm looking at sharedpreferences now, it's really preferable indeed for storing info persistently. – Erlend K.H. Jan 06 '19 at 15:04
  • 1
    @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. – Aloha Jan 06 '19 at 15:25
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.

LarsH
  • 27,481
  • 8
  • 94
  • 152
Lalit Sharma
  • 1,142
  • 1
  • 15
  • 32
  • 2
    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. – LarsH Aug 17 '15 at 16:10
  • 1
    @LarsH ha ha ha No issues dear. – Lalit Sharma Sep 08 '15 at 12:48
  • 2
    I edited it and removed the downvote. – LarsH Sep 08 '15 at 13:52
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();
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

Vitaly Zinchenko
  • 4,871
  • 5
  • 36
  • 52
3

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

Have a look for MultipleThemeMaterialDesign

Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
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);
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
dondondon
  • 881
  • 8
  • 4
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();
duongnx
  • 21
  • 3
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.

Sumit Dhaniya
  • 596
  • 6
  • 14
0

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

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();
}
arlomedia
  • 8,534
  • 5
  • 60
  • 108
  • 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);``` – Abandoned Cart Jul 22 '16 at 19:41
  • 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. – Freddie Aug 01 '19 at 01:50