I am trying to implement a theme change in Android since I need to change the look and feel of the whole application dynamically as a result of some asynchronous action.
I have several themes like this:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="preferenceTheme">#style/PreferenceThemeOverlay.v14.Material</item>
</style>
<style name="YellowTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorYellowLight</item>
<item name="colorPrimaryDark">#color/colorYellowDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="preferenceTheme">#style/PreferenceThemeOverlay.v14.Material</item>
</style>
<style name="GreenTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorGreenLight</item>
<item name="colorPrimaryDark">#color/colorGreenDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="preferenceTheme">#style/PreferenceThemeOverlay.v14.Material</item>
</style>
and I am using MutableLiveData to store some object which is used as a basis for the theme change:
val dayTypeDTO = MutableLiveData<DayTypeDTO>()
I am then observing this object and when it's value changes, I need to change the app theme:
cache.dayTypeDTO.observe(this, Observer { dto ->
if (dto != null) setTheme(dto.theme)
})
The problem is that the theme change should happen after the view is inflated. Otherwise, it is not applied. But since I am registering the observers after the view is created and due to the dynamic change which cannot be predicted, I cannot really do it this way.
I know I can call activity.recreate() but this just results in an infinite loop.
Can someone suggest how to achieve this theme change so all components using colorPrimary are updated as a result of this observable action? Preferably other then changing them manually one by one.
Add an onGlobalLayoutListener to your view to ensure that the layout is inflated before setting the theme on it:
https://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener
Related
I have a specifix view that needs to use windowIsTranslucent = true. This generates some problems that I'm having hard time to understand... the specific one that I'm trying to resolve now is why the transition in/out changes to slide up / down only because of this style.
I want to use windowIsTranslucent = true but with the standard activity transition animation.
These are relevant styles
<style name="AppTheme.AppCompat.Translucent" parent="AppTheme">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">#color/opacity_background</item>
<item name="android:colorBackgroundCacheHint">#null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:statusBarColor">#07819A</item>
<item name="android:actionMenuTextColor">#color/White</item>
<item name="android:windowDisablePreview">true</item>
<item name="colorPrimary">#07819A</item>
<item name="colorPrimaryDark">#006471</item>
<item name="android:fastScrollTextColor">#ffffff</item>
<item name="android:dropDownListViewStyle">#style/FilterSpinnerStyle</item>
<item name="android:actionOverflowButtonStyle">#style/AppTheme.OverFlow</item>
<item name="android:autofilledHighlight">#android:color/transparent</item>
</style>
What I have tried witout success:
Added this line to AppTheme.AppCompat.Translucent
<item name="android:windowAnimationStyle">#+android:style/Animation.Translucent</item>
Added this line to AppTheme.AppCompat.Translucent
<item name="android:windowAnimationStyle">#+android:style/Animation.Activity</item>
Used CurrentActivity.OverridePendingTransition(someId, someId); and it works. The problem is that animation is a little laggy when I try to mimic standard animation and I also have the problem to use the correct animation for each version of Android.
I tried some other things that I don't have at the top of my head right now... the thing is that nothing seems to be enough :(
You can override the default transition animation for your app by declaring a custom style with your desired animations like this:
<style name="CustomAnimation" parent="#android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">#anim/your_desired_animation</item>
<item name="android:activityOpenExitAnimation">#anim/your_desired_animation</item>
<item name="android:activityCloseEnterAnimation">#anim/your_desired_animation</item>
<item name="android:activityCloseExitAnimation">#anim/your_desired_animation</item>
</style>
And then by setting it to your base theme you can set the custom transition animation for your app
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
<!-- Customize your theme here. -->
...
<item name="android:windowAnimationStyle">#style/CustomAnimation</item>
</style>
Thus you can declare your own transition animations. Even if the standard animations not working, you can always make your own like the standard one.
I have basic knowledge of animations, but how to set globally same animations for opening/closing dialogs within my app?
I have a preference screen with toolbar on top (with some menu items). And clicking on menu, or preferences opens dialogs.
How to set same dialog animations within my app, or at least within certain activity? So whatever dialog I open, it will always be same animation?
You can create a theme for your dialogs with the desired animations and apply it to your app's theme in styles.xml like this:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<!-- Apply your custom dialog theme here. -->
<item name="android:dialogTheme">#style/CustomDialog</item>
<item name="android:alertDialogTheme">#style/CustomDialog</item>
</style>
<style name="CustomDialog" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowAnimationStyle">#style/CustomDialogAnimation</item>
</style>
<style name="CustomDialogAnimation">
<item name="android:windowEnterAnimation">#android:anim/slide_in_left</item>
<item name="android:windowExitAnimation">#android:anim/slide_out_right</item>
</style>
That will apply the enter and exit animations to all of your dialogs โ๐ปI just tested it myself and it worked, let me know if it works for you ๐
Use this in alert dialog,
if (alertDialogBuilder.getWindow() != null)
alertDialogBuilder.getWindow().getAttributes().windowAnimations = R.style.DialogTheme; //style id
In styles.xml define above Dialog Theme like below:
<style name="DialogTheme">
<item name="android:windowEnterAnimation">#anim/slide_left</item>
<item name="android:windowExitAnimation">#anim/slide_right</item>
</style>
Happy Coding :)
I'm trying to edit theme like this:
<style name="MyAppTheme" parent="#android:style/Theme.DeviceDefault.Light.DarkActionBar">
<item name="android:background">#color/colorPrimary</item>
</style>
But this colours my whole activity. Instead I want only action bar to be coloured with my custom color. How to achieve this?
Edit:
I've managed to achieve desired result by using Theme Editor and creating custom theme with it.
Try this
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
and please read this for more info
Use AppCompat theme. Specify colorPrimaryatribute since the ActionBar background color depends on it.
<style name="MyAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
</style>
Also you can try it like this ,
Use a parent style and declare your whatever Widget style inside the parent one.Here use android:actionBarStyle
See the example below
<resources>
<style name="MyTheme" parent="#android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">#style/MyActionBar</item>
</style>
<style name="MyActionBar" parent="#android:style/Widget.Holo.Light.ActionBar">
<item name="android:background">ANY_HEX_COLOR_CODE</item>
</style>
</resources>
set MyTheme in your activity
Note : ActionBar will not work for your target environment which is at API level 10
So here is the situation:
I have DogActivity and FavoritesActivity. DogActivity is just a ListView. When you click on a Dog in the list, it takes you to FavoritesActivity.
I want to have a number of themes ready to go. They donโt need to be dynamically generated. They can already exist in XML form.
Depending on which dog the user selects from the list, I want to have the FavoritesActivity shown in one of my pre-existing themes.
I hear talks about ContextWrapper, but I am not sure how to apply it. Any thoughts on how I may accomplish this?
Details:
Here is the usual single theme:
for v21/styles.xml
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="android:colorControlHighlight">#color/colorAccentLight</item>
<item name="android:colorControlNormal">#color/colorAccent</item>
<item name="android:itemTextAppearance">#style/AppTheme.itemTextStyle</item>
<item name="popupMenuStyle">#style/PopupMenu.MyAppTheme</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:colorControlHighlight">#color/colorAccentLight</item>
</style>
<style name="AppTheme.itemTextStyle" parent="#android:style/TextAppearance.Widget.IconMenu.Item">
<item name="android:textColor">#color/colorPrimary</item>
</style>
</resources>
for styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
Want I want to do:
Essentially I just want to change the colorPrimary, colorPrimaryDark and colorAccent on the fly and have all the styles and themes and XML layouts that use them to change. So if I can change those colors before I launch FavoritesActivity then that would solve my problems.
You can just send the dog type as an Intent extra, and then use the setTheme() method to set the appropriate Theme.
For this example, suppose you just have two Themes:
<style name="AppThemeOne" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
<style name="AppThemeTwo" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/colorPrimaryCustom</item>
<item name="colorPrimaryDark">#color/colorPrimaryDarkCustom</item>
<item name="colorAccent">#color/colorAccentCustom</item>
</style>
Then, in DogActivity, set an Intent Extra to the Dog type the user selected from the ListView:
Intent intent = new Intent(DogActivity.this, FavoritesActivity.class);
intent.putExtra("dog_type", "terrier");
startActivity(intent);
Then, in FavoritesActivity, load the correct Theme:
#Override
protected void onCreate(Bundle savedInstanceState) {
String dogType = getIntent().getStringExtra("dog_type");
if (dogType.equals("terrier")) {
setTheme(R.style.AppThemeOne);
} else {
setTheme(R.style.AppThemeTwo);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.favorites_layout);
//.....
}
I've accomplish it quite simply on my latest project, you just have to set the values on the theme via Java. Like the following code:
public class FavoritesActivity extends AppCompatActivity { // it can be Activity too
#Override
public void onCreate(Bundle savedInstanceState) {
if( ... check condition to change theme ) {
// this will replace every value from FavoritesActivity theme by the
// the values on `other_style` theme.
getTheme().applyStyle(R.style.other_style, true);
}
// call super AFTER applying the theme
super.onCreate(savedInstanceState);
.. carry on your normal stuff
}
that's very useful because you can very easily replace just a few values and keep the rest as the original, or change everything from the original. It all depends what argument you pass to the applyTheme method.
Also is great you don't have to mock with ContextThemeWrapper. The values are there on the theme and that's it.
https://developer.android.com/reference/android/content/res/Resources.Theme.html#applyStyle(int, boolean)
You can use this.recreate() method for this.
Based on https://stackoverflow.com/a/29722976/7547681 answer.
I am studing Support Design liabrary.I cannot understand the use of enterAlwaysCollapsed.It seems it will influece on pull down,but I tried and find nothing changed.
So,what is the use of enterAlwaysCollapsed,could you show me with a demo?
Let me answer it myself.
First the activity theme is AppTheme.
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
And then Don't set fitsSystemWindows in xml.
Last use scroll flag scroll|enterAlways|enterAlwaysCollapsed.
The final effect is below