37

There are lots of docs and tutorials on creating or customising an Android theme style via XML, but have not been able to find a way to create it in code. Any ideas on how to create the style in code rather than xml?

This is the example XML, need to create this programmatically in code:

<resources>
  <style name="AppTheme" parent="android:Theme.Material">
    <item name="android:colorPrimary">@color/primary</item>
    <item name="android:colorPrimaryDark">@color/primary_dark</item>
    <item name="android:colorAccent">@color/accent</item>
  </style>
</resources>
Mercury
  • 711
  • 1
  • 11
  • 21
  • 1
    Any reason why you want it to be done programmatically? If you just want to dynamically change style at runtime, you may check this link http://stackoverflow.com/questions/3241729/android-dynamically-change-style-at-runtime. And if you want to simply retrieve style attribute in code - check this: http://stackoverflow.com/questions/13719103/how-to-retrieve-style-attributes-programatically-from-styles-xml – random Sep 26 '15 at 13:25
  • @random no specific reason, tired of xml so just playing around to see if it is possible to create a theme on the fly – Mercury Sep 26 '15 at 15:01
  • 11
    @random I have a reason. I need to give color dynamically, currently colors is not decided. So, I write theme like above in xml but I want to give color dynamically at first screen only. So that after it will apply in all screen. – Ajay Sharma Nov 03 '15 at 11:12
  • I am interested in this too for.the same reasons as ajay. But it seems there is no way. – Nathan Nov 30 '16 at 08:07
  • @AjaySharma But you can set a style in xml & load from code to solve yours – Renjith Thankachan Dec 10 '16 at 12:30
  • @Mercury Updated my answer with more details on calling flow, that its not possible without an xml resource to apply theme to window/activitiy – Renjith Thankachan Dec 13 '16 at 17:17
  • Yes, It is not possible. So, I make a CSS class and make some method to apply colors on controls. Like create a ApplyButtonCSS method that take a button object and whenever you use button in your activity just call this method to set button colors. This way I find a alternative way to give colors to each type of controls. – Ajay Sharma Dec 14 '16 at 10:15
  • any updates on this? still not possible to create a Theme at runtime and then apply it through setTheme ? – Alberto M Jun 06 '18 at 16:07
  • Can anyone tell me, I want to change the style attributes values dynamically? Is it possible? – Vivek Pratap Singh Mar 26 '20 at 03:03

2 Answers2

7

TL;DR: Just no, you can't. That's how Android works and will never be changed.


There is a plain and simple reason why programmatically creating Android themes will never be possible. That is, when an app is starting, Android creates a dummy window that shows the app's android:windowBackground. At this time, the app process is still being initialized, and when app execution starts and Activity.onCreate method of the activity being launched returns, the dummy window is replaced with the app window, which shows that activity.
Therefore, the fact is, since android:windowBackground is set by themes but Android has to access it before the app is even started, themes have to be resources, so that they can be accessed from any process (including the system process, of course.)

Moreover, themes are resources, and as such they're completely immutable. That's how Android works. It cannot just be changed all of a sudden, and it's very unlikely it will ever. An additional reason why resources will never be able to be dynamically modified is due to the direct implication such that the APK itself would need to be modified as well — and that's not the way APKs are meant, either.

One may argue “everything must be done programmatically, under the hood.” Well, that's correct. However, the android.content.res.Resources.Theme class is final for a reason, which is to make sure that nothing be overridden, so that its behavior is guaranteed to reflect what resources say, which is fundamental for the system process accessing the android:windowBackground of the activity theme to be coherent with the way the app will behave once started. The same holds when it comes to the android.content.Context.obtainStyledResources method, which is final too. Indeed, if the app could override that method, it would be able to return values that do not match resources, which is a problem as that would happen only once the app process is started, when the original, real android:windowBackground had already been shown.

Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31
  • 2
    This argument generally makes sense, but it makes me wonder why the following method exists: https://developer.android.com/reference/android/content/res/Resources#newTheme() Resources exposes a `newTheme()` method. – Zach Sperske Apr 21 '21 at 00:36
  • 1
    [1/2] @ZachSperske, although I wasn't aware of the `newTheme()` method, what I've described is fundamental to the way Android actually works, so my answer should stay correct. What I guess the method you mentioned is for is creating a `Theme` object for an implementation of the `Resources` class. – Davide Cannizzo Apr 21 '21 at 12:16
  • 1
    [2/2] This way, it should be possible to implement a custom `Resources` class that provides custom values for attributes, and creating a `Theme` object for it you'll be able to set it as a theme for an activity, thus actually creating a theme at runtime. However, the activity's theme specified in the manifest must be one defined in XML and while the app's process isn't created yet, the activity will still be showing the XML theme, while the custom theme created programmatically will only be able to be shown once the activity is fully loaded – Davide Cannizzo Apr 21 '21 at 12:18
  • Cool! That makes sense. I've been trying to figure out how to apply a theme to a Drawable at runtime and was struggling. I was able to achieve it by using the `newTheme()` method to create a `Theme`, applied the style I wanted to the theme via `programmaticTheme.applyStyle(R.style.IconPrimaryTheme, force = true)` and then used `ResourcesCompat.getDrawable(resources, R.drawable.myIcon, programmaticTheme)` to get a drawable in the Theme I wanted. – Zach Sperske Apr 21 '21 at 14:05
  • [1/2] @ZachSperske, I'm happy to hear you back saying that you've figured out how to do what you've been hoping to. However, I think it could've been done better a different way — rather than creating a different theme programmatically from an XML style and applying it to the `Drawable` directly, it'd be saner to just apply the XML style as a theme to the `View` that actually holds the drawable. – Davide Cannizzo Apr 21 '21 at 14:38
  • [2/2] You may want to use the XML `android:theme` attribute for this, if that view is in an XML layout, or you can programmatically create a themed context using the `ContextThemeWrapper` class and create the view with that context. I'm always writing two comments at a time LOL – Davide Cannizzo Apr 21 '21 at 14:38
  • Woah thanks! I was stuck thinking that you could only use `ContextThemeWrapper` when creating your View and that it in many cases it would be too late to use it. But then I realized you can use that wrapped to fetch your Drawable! – Zach Sperske Apr 21 '21 at 14:47
  • ` fun Context.getThemedDrawableFromAttribute(@AttrRes attrResourceId: Int, @StyleRes drawableStyle: Int) : Drawable { val wrapper = ContextThemeWrapper(this, drawableStyle) val drawable = wrapper.getDrawableFromAttribute(attrResourceId) return drawable } ` – Zach Sperske Apr 21 '21 at 14:47
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/231422/discussion-between-zach-sperske-and-davide-cannizzo). – Zach Sperske Apr 21 '21 at 14:49
6

short answer: Its not possible as programmatically create a theme & set as application theme ( even if we achieved to create a Theme object) without a theme resource id.

Details:

when you call setTheme the function ineffect a method of ContextWrapper, which at the end calls AssetManager with resource id pointer, AssetManager class holds the method for applying application theme, which is JNI call

native static final void applyThemeStyle(long theme, int res, boolean force);

As above we can only pass a resource id to apply the themestyle. But possible options are

  1. Though its limited to Window class feature constants. We can use setFeatureDrawable & feature constants to set some drawables like, FEATURE_ACTION_BAR, FEATURE_CONTEXT_MENU etc..
  2. Using setTheme function from activity, we can set the theme from style resource, that will solve the problem mentioned in comments by AjaySharma & Nathan
Renjith Thankachan
  • 4,178
  • 1
  • 30
  • 47