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.
Related
I have a logo on my splash screen and two files:
logo_night.png and logo_day.png
How can i change this logo that will change depending on the theme of the app using these two files?
The usual way is to use a night resource qualifier, so if you're using a dark theme it pulls the named resource from the equivalent night folder if it exists there.
You have three basic options with this I think:
just call them both logo.png, but put one version in drawable and the other in drawable-night. That way, whenever you refer to #drawable/logo it'll pull the correct version depending on the theme. If you have multiple density versions, in drawable-hdpi etc, you'll need them in drawable-night-hdpi etc. (the qualifier order matters)
if you're making your splash in the usual way, where you create a splash drawable with a background layer and your logo drawable on another layer, you could just make a night variant of this instead (in drawable-night). Each version can explicitly refer to logo_day or logo_night and you can just keep those PNGs in the normal drawable folder
or you can make a night version of your splash theme, and explicitly set the background to the dark version of your splash drawable instead. Both your splash drawables can stay in the normal drawable folder
So basically, something's gotta get a version in a night-qualified resource folder, so it can provide an alternative resource for dark mode. It's up to you which resource in that hierarchy you want to make the night version of (or you can do it for all of them if you want).
Also bear in mind that your splash theme (if you're doing it that way) follows the system's dark mode setting. If you're implementing a toggle in your app, and you set it to dark mode, the splash will still show up as the light variant if that's what the device is set to. That theme is set before your app starts and gets to work out if it should be in dark or light mode
Since you are using png format. You can call .getTheme() on context object to get current theme, make an if statement to decide which png to show.
For example in activity you can get theme by just calling getTheme() because this.getTheme() is implied.
Other possibility is to switch to vectors where you can define different colors for different themes they will change automatically when user changes theme. This is probably better and more scalable solution. You can find much more details here.
I am working on an Android project where I need to support switching between Dark/Light themes based on system setting preference.
I have changed App Theme from Theme.AppCompat.Light.NoActionBar to Theme.AppCompat.DayNight.NoActionBar.
I am using this API on Application onCreate()
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
App theme is getting updated only when I restart app but does not update when I am within the app when theme is changed from system setting.
Am I missing any config changes?
I believe that the issue lies with the android:configChanges tag. This is because the activity needs to be recreated when the theme is changed. You will have to remove the tag and use onSavedInstanceState to save the state of your current activity.
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
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
For some Activity I'm using #Theme/Dialog, but this appears like old UI 2.3.
If I set the Theme of the Activity in the Manifest as Holo/Dialog, then this work fine, but of course this will not work with older devices.
How to force Holo Theme when available?
I tried with a custom MyTheme, but all the activity that have #android:style/Theme.Dialog, continue to appear like old theme.
My activity look like below:
<activity
android:name=".audio.TempoManager"
android:label="Tempo Manager"
android:theme="#android:style/Theme.Dialog" />
if I change it to #android:style/Theme.Holo.Dialog then on old device the activities will not be open as dialog but as simple activity.
refer to this blog post. it answers your question: holo-everywhere
mainly:
1. you write a theme.xml file defining a MyTheme in res/values/ for all pre-3.0 android versions.
2. write a themes.xml file defining the MyTheme in res/values-v11/ for 3.0+ android versions.
3. in the AndroidManifest.xml define the application theme to be MyTheme.
the first theme inherits from #android:style/Theme.
the second theme inherits from #android:style/Theme.Holo.
If ICS is available on the device it will default to Holo,
If you want to create for instance the ICS font universally on all devices you will need to provide the ttf font file asset and use it in a custom textview and/or buttons etc..