How to programmatically create an Android theme style? - android

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>

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.

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
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..
Using setTheme function from activity, we can set the theme from style resource, that will solve the problem mentioned in comments by AjaySharma & Nathan

Related

Resource reference as attribute value not considered for resource qualification

Assuming related to How to setup night mode drawables as expected
I'm trying to implement the new AppCompat DayNight theme in my Android app while providing a theme switch in the settings.
Scenario:
As said theme switch was used, the "settings" activity recreated itself as a result of a call to AppCompatDelegate#setDefaultNightMode and AppCompatDelegate#applyDayNight (the latter is available to AppCompatActivity through its getDelegate() method).
The text color was inverted, the background changed and the back arrow's color changed as well. So far so good.
As the user finished changing the app's theme, he would close the activity and continue using the app. What does he expect to happen? Exactly, the main activity should have changed its theme as well. How do we do this? Activity#recreate in Activity#onActivityResult.
The call to AppCompatDelegate#setDefaultNightMode triggers a change in the configuration, which, afaik, is being applied either by AppCompatDelegate#applyDayNight or as soon as another Activity resolves its theme & configuration in Activity#onCreate. The first applies for the settings activity, the second is what is happening now.
Now to the critical part: the app's resources are dispatched a configuration change, and are now being checked against their validity (resource qualification), and if that fails, they will be cleared from the cache so that new resources (in this case, drawables) are created in order to fully reflect the new theme & configuration.
Now as I've said, the back arrow in the settings activity did change its color, but this was not my own drawable, it was the default one that you get with getSupportActionBar.setDisplayHomeAsUpEnabled(true).
In the main activity however, I did use my own drawables, for menu icons and other ui elements for example. These just didn't change. I'm using vector drawables, which get their color from the fillColor attribute.
TL;DR
To make the DayNight theme work for me, I have defined theme attributes for certain colors used throughout my app, also for icons (drawables). These attributes are populated by the one unified theme I'm using, and are referring to colors from values-night or values-notnight depending on the current configuration. This is a redundant indirection, but makes for a better style, at least in my opinion, when used together with default theme attributes such as textColorPrimary etc..
But: as it turns out, it doesn't work. Going the attribute-way, those attributes, which are in the end colors, loose their quality of being resource qualified (night or notnight) and therefore pass the "validity test" performed on configuration change, even if that's not what's supposed to happen. Thus, after I have changed the night mode, I end up with the old, wrong drawables.
To fix this I could e.g. simply
replace android:fillColor="?attr/myCustomAttributeColorXEitherNightOrNot" with android:fillColor="#color/colorXEitherNightOrNot".
But is that really what this is supposed to be like? Am I missing some important attributes-principle here, or is it a bug? I'd like to hear some thoughts on this.
Its pity but suppose that using on bitwise operations with Configuration still only normal way to provide night theme. See example: https://gist.github.com/slightfoot/c508cdc8828a478572e0

Modify Android Theme Programmatically

A similar question to this has been asked many times; however, an answer has not been given that addresses my situation. I need to dynamically change an application's theme based on color values that are being returned from an API call. I then need to change the theme colors of the app based on the values returned. Therefore, I have no way of saving the colors in a style XML file. Can this be done?
I have a base activity, and my plan is to set the app theme from there for all the activities.
Unfortunately, I did not find an easy way to do this. I created a ThemeColor class which holds all the colors returned by the API. Then for each activity I have to go through every widget and style it.
Example:
getActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor(themeColor.getActionBarColor)));
this.getWindow().getDecorView().setBackgroundColor(Color.parseColor(themeColor.getBackgroundColor()));
etc
I was not able to locate a simple way to resolve this issue either. By creating a ThemeColor class that holds all the colors returned from the API. Next for each activity I needed to address each widget separately as well as style it.

Choosing a style / theme programmatically at run time

I have device A and device B.
I can easily detect if the app is running on device A or on device B.
Now what I need is to use on theme (styles) for device A and other on device B.
How can I do this?
In your Activity.onCreate(), you can call setTheme() to set the theme you would like to use. Note this must be done before you call setContentView() or otherwise create your UI.
Keep in mind that when the user launches your app, the system will show a preview of it while this happen. This previous is based on creating a window that matches the theme declared in your manifest. You want this to match as closely as possible the themes you are going to set in your onCreate() to make the transition to your app as smooth as possible.
If you want your theme to vary based on some device configuration -- such as platform version or screen size -- you can do this all through resources. Just declare different versions of your theme for the different configurations you want. The file layout would be something like:
values/
styles.xml # Required default theme
values-v11/
styles.xml # Theme when running on Android 3.0 or higher
values-xlarge/
styles.xml # Theme when running on an xlarge screen
The -v11 allows you to have a version of the theme that uses a new theme when running on newer platforms while reverting to something compatible on older versions. For example in the values-v11 style your theme's parent could be the new #android:style/Theme.Holo, while the basic one would inherit from the older #android:style/Theme.
Also Android 3.0 gives you a way to change your theme at runtime, by asking that your activity being restarted like when a configuration change happens: http://developer.android.com/reference/android/app/Activity.html#recreate()
After calling that, the new instance of the Activity that gets created can call setTheme() with a different value (based for example on information in the saved instance state or a shared preference) than the theme that was previously being used.

Consistent UI color in all Android devices

I noticed the UI color (eg Button background/text color) all changes from device to device, based on the current theme that is being used in a device.
What is the best practice to apply custom UI colors for Android app, so that I have same color scheme for my app in all Android devices. I can set text/background color on a UI item. I'm wondering if there is a single place where I can define all the colors which will override the current theme applied on the phone.
thx.
Yes, there is a single place where you can define these values for your app. See Styles and Themes in the Android docs for how it works.
A style is just a mapping of values to predefined names. If you find yourself repeating a number of common attributes in your layouts, you can factor that out into a style. For example, you might have a special button style that defines a specific background and text color.
A theme is a sort of meta-style. It can be applied to an Activity or even a whole application through your AndroidManifest.xml. Among other things it defines the default styles for widgets and values that control other parts of the look and feel for your UI.
When you're trying to blend in with the system in an otherwise custom UI for your app, you can query the current theme for values. Just like you use the # reference syntax #android:drawable/foo when referring to a system resource, you can use the syntax ?android:attr/foo when you want to use the value stored in the system theme attribute foo.
In your case, if you want to change the primary text color across your app, apply a custom theme that sets the attribute textColorPrimary. If you just want to be sure that an element of your app is using the primary text color as defined by the device your app is running on, you can set android:textColor="?android:attr/textColorPrimary". The same principles apply elsewhere as well.
If you want to see what attributes are used in the system, they are defined as part of the Android framework in this file: frameworks/base/core/res/res/values/attrs.xml. Look at the children of the XML element <declare-styleable name="Theme"> at the top. To see examples of what the system sets these to, see themes.xml in the same directory. Finally, not all of these attributes are public - non-public attributes cannot be set by an app, they're implementation details of the Android framework. See public.xml for the complete list of which attributes are available for use in apps.
Best practice is to apply a custom theme to your application, and override as much of the default properties as you need.
Almost everything can be changed, except
The Menu
Some properties of AlertDialog (these can be changed using a custom dialog)
OS provided views such as the Quick Search Bar (QSB)
If you like the look of the default SDK resources then you can find these in sdk_folder/platforms/android-9/data/res/ (replace 9 with the SDK version you want the resources from) - copy the ones you want into your App and reference those.
You can take a look at the theme the SDK uses:
themes.xml
styles.xml

Multi theme support in android app

Did anyone implemented multi theme support for android app? Are there any common practices for this?
Thanks for any advice.
UPD: The main problem for now is that android's theme engine doesn't support selectors like in CSS. For example if I have two views with "background" attribute there's no way to make theme engine distinguish those ones and set different backgrounds. I can specify different style for each view but this approach lacks flexibility 'cause it's impossible to apply style for whole activity at once.
As far as I know, there is no way to set a theme to the whole application in on line of code. If you want to change the theme of an activity, you need call setTheme() in its onCreate() method, BEFORE calling setContentView. So to make it easier for you, you could do a switch on all your themes, and select one in regards of what the user has selected. Now, if you want it to apply easily to all your activities, you could make all your activities be a subclass of a custom Activity in which you would only set the theme. Activity <-- ThemeActivity <-- all your Activities

Categories

Resources