I have made a few apps that support multiple themes, but I always had to restart the app when user switches theme, because setTheme() needs to be called before setContentView().
I was okay with it, until I discovered this app. It can seamlessly switch between two themes, and with transitions/animations too!
Please give me some hints on how this was implemented (and animations too). Thanks!
#Alexander Hanssen's answer basically has answered this...
Don't know why it was not accepted... Maybe because of the finish()/startActivity().
I voted for it and I tried to comment but cannot...
Anyway, I would do exactly what he described in terms of styles.
<style name="AppThemeLight" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat">
<!-- Customize your theme here. -->
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<!-- This will set the fade in animation on all your activities by default -->
<style name="WindowAnimationTransition">
<item name="android:windowEnterAnimation">#android:anim/fade_in</item>
<item name="android:windowExitAnimation">#android:anim/fade_out</item>
</style>
But instead of finish/start with new intent:
Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();
I would do:
#Override
protected void onCreate(Bundle savedInstanceState) {
// MUST do this before super call or setContentView(...)
// pick which theme DAY or NIGHT from settings
setTheme(someSettings.get(PREFFERED_THEME) ? R.style.AppThemeLight : R.style.AppThemeDark);
super.onCreate(savedInstanceState);
}
// Somewhere in your activity where the button switches the theme
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// decide which theme to use DAY or NIGHT and save it
someSettings.save(PREFFERED_THEME, isDay());
Activity.this.recreate();
}
});
The effect is as shown in the video...
The transition/animation makes the theme change seamless when you restart the activity, and this can be done by adding the items "android:windowanimationStyle" to your themes, and then referencing a style where you specifiy how the Activity should animate when it enters and exits.
Note that this makes the animation apply on all activities with that theme.
<style name="AppThemeLight" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat">
<!-- Customize your theme here. -->
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<!-- This will set the fade in animation on all your activities by default -->
<style name="WindowAnimationTransition">
<item name="android:windowEnterAnimation">#android:anim/fade_in</item>
<item name="android:windowExitAnimation">#android:anim/fade_out</item>
</style>
Then, when you want to change theme you could do this when clicking a button:
AppSettings settings = AppSettings.getInstance(this);
settings.set(AppSettings.Key.USE_DARK_THEME,
!settings.getBoolean(AppSettings.Key.USE_DARK_THEME));
Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();
Then in your onCreate method, use the setTheme() to apply the theme that is currently set in AppSettings like this:
AppSettings settings = AppSettings.getInstance(this);
setTheme(settings.getBoolean(AppSettings.Key.USE_DARK_THEME) ? R.style.AppThemeDark : R.style.AppThemeLight);
super.onCreate(savedInstanceState);
setContentView(<yourlayouthere>);
Check out this gist for reference: https://gist.github.com/alphamu/f2469c28e17b24114fe5
for those who are trying to find solution for android version 10 or updated.
to set dark/light mode use this:
AppCompatDelegate.setDefaultNightMode(state) //state can be AppCompatDelegate.MODE_NIGHT_YES or AppCompatDelegate.MODE_NIGHT_NO
it will change the display of your app but with a flicker
to avoid the activity recreation flicker (for smooth transition), in your activity add the below method
#Override
public void recreate() {
finish();
overridePendingTransition(R.anim.anime_fade_in,
R.anim.anime_fade_out);
startActivity(getIntent());
overridePendingTransition(R.anim.anime_fade_in,
R.anim.anime_fade_out);
}
setTheme() before super.onCreate(savedInstanceState) in GKA answer is perfect approach and work well, thanks to GKA.
but it creates new instances for all resources again, including activities, fragments, and recycler views. I think it may be heavy work and cause to loss of some saved data like local variables.
accourding to google document: https://developer.android.com/reference/android/app/Activity#recreate()
Cause this Activity to be recreated with a new instance. This results
in essentially the same flow as when the Activity is created due to a
configuration change -- the current instance will go through its
lifecycle to onDestroy() and a new instance then created after it.
there is another approach that you can change the theme programmatically with code (Java or Kotlin), in this approach you don't need to recreate all resources, and also you can use custom animation like ripple.
check my GitHub library:
https://github.com/imandolatkia/Android-Animated-Theme-Manager
in this library, you can create your custom themes and change them dynamically with ripple animation without recreating any resources.
Simply efficient one liner in fragment:
requireActivity().recreate();
For activity:
recreate();
There isn't anything preventing you from calling setTheme() and then setContentView() again. You'll just need to restructure your app a bit so that, if you change the theme, you need to reinitialize any member variables you might have that are holding references to View objects.
Related
In my Xamarin Forms Android app, I would like to change the color of the splash screen based on a theme that the user has selected at run time. This change should impact the next run of the app.
I have tried using the built in splash_screen.xml and using a Splash.axml file, but I cannot figure out how (if possible) to set the background color of the splash screen to a color defined in a theme.
Is this possible?
I have tried using the built in splash_screen.xml and using a Splash.axml file, but I cannot figure out how (if possible) to set the background color of the splash screen to a color defined in a theme.
You can define themes for your splashScreen and then set android:windowBackground to a specific color:
styles.xml:
<resources>
<style name="AppTheme">
<item name="android:windowBackground">#color/colorPrimary</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="AppTheme2">
<item name="android:windowBackground">#color/colorAccent</item>
<item name="android:windowNoTitle">true</item>
</style>
...
</resources>
SplashScreen.cs( don't forget to set SplashScreen activity to be mainlauncher):
[Activity(Label = "SplashActivity",MainLauncher =true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
ISharedPreferences preferences=GetSharedPreferences("SplashThemeId", FileCreationMode.Private);
var themeName=preferences.GetString("themeName","AppTheme");
int themeId = Resource.Style.AppTheme ;
switch (themeName)
{
case "AppTheme":
themeId = Resource.Style.AppTheme;
break;
case "AppTheme2":
themeId = Resource.Style.AppTheme2;
break;
}
SetTheme(themeId);
base.OnCreate(savedInstanceState);
...
As you noticed, I use SharedPreference to store the user selected Theme in Runtime. You can also use Android Settings for the same purpose.
Here is the complete demo. It's native android project, but it's the same for forms project.
After all of the research I've done and the things I've tried in code, I am concluding that it is not possible to dynamically set the color of the initial activity. I am going with a neutral color for the splash.
Hi just as the title suggests I'm sure this should be simple but alas it isn't working for me, I'm not looking for anything fancy like onTabSelected etc I just want to set the color of the entire widget based on the theme, right now im trying to do the following, which I'm not sure if is correct but its not working,
It looks like this is what I need:
<item name="tabBackground">#color/colorPinkPrimary</item>
But I've tried adding this to my theme in 2 ways and neither work, the first way was to just add this directly to my theme as it is but no joy, the second way I tried was to add another theme.
<style name="CustomTabLayoutStylePink" parent="Base.Widget.Design.TabLayout">
<item name="tabSelectedTextColor">#color/selected_textPink</item>
<item name="tabIndicatorColor">#color/colorPinkAccent</item>
<item name="tabTextAppearance">#style/CustomTabTexStylePink</item>
<item name="tabBackground">#color/colorPinkPrimary</item>
</style>
And refer it from my theme like this (just to reiterate I don't know if this is best practice or even feasible, it just looked right)
<item name="android:tabWidgetStyle">#style/CustomTabLayoutStylePink</item>
The one that works is the following:
<item name="android:background">#color/colorPinkPrimary</item>
This works for every view in my entire app even down to the preference activity textviews.
tabBackground should be in your XML file and not inside the style.
<android.support.design.widget.TabLayout
...
app:tabBackground="#color/colorPinkPrimary"
...
/>
ok so what i wanted wasn't possible instead ive set up a preference to record what theme should be applied, and depending on what theme is selected, color the tabs accordingly like this
private void setupTabBackground(){
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext());
// Create a new boolean and preference and set it to true
prefThemeString = sharedPreferences.getString("THEME SELECTED","BLUE");
switch (prefThemeString){
case "BLUE":
tabLayout.setBackgroundColor(getResources().getColor(R.color.colorPrimaryBlue));
break;
case "PINK":
tabLayout.setBackgroundColor(getResources().getColor(R.color.colorPinkPrimary));
break;
}
}
i think there should also be a shared preference listener in this activity but i can address that later
I have two apps and when i switch from one app to another a black screen is displayed for a while and then the 2nd application starts, can we stop this from happening?
One of the simplest way is to move all the expensive (time-consuming) processing from your activity's onCreate and onStart method to onResume method. By this, your newly launched activity will be visible right after its launched but then will take a little extra to make it available for user to interact. Further, I would suggest you to move all the heavy lifting in AsyncTask for smoother UI experience.
You can also try this theme for your Activity
<resources>
<!-- Base application theme is the default theme. -->
<style name="Theme" parent="android:style/Theme" />
<style name="Theme.MyAppTheme" parent="Theme">
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowBackground">#drawable/my_app_background</item>
</style>
</resources>
If you switching between fragments through screen manager kind of class, which uses enums and fragment transaction to switch between fragments; make sure you put a break statement, after each enum case. This solved my issue. Hope it helps!
Whenever i launch my app, a white screen appears in the beginning with the title bar. I don't want this screen to be appear in my app. I have read previous questions, but answers are not clear to me.
I'm also using splash screen, but white screen appears before that.
I don't want to change the theme style, because it either increases the minimum sdkVersion or changes the style of edittext, buttons, checkboxes etc
Please help me to keep me out of this.
Thank you.
Preface: For questions like this you should post your starting activities xml and the onCreate() and associated methods.
When android starts your application it will typically use a black view to indicate that it is launching, this my change to white with your theme/style selected. If you are loading the view correctly then you should only see this blank (white or black) page for 50-200 ms (I can't find the google document for this right now). If you are doing a lot of work in your onCreate method then it will take longer.
Typically to make my views display faster I will simply do the majority of the linking work after it has loaded. ex:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.initial_activity_layout);
//We use a handler so that the activity starts very fast
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
delayedInit();
}
}, 100);
}
Additionally, mobile applications should typically not have a splash screen unless they take quite a while to load the contents (e.g. games, first time launch files, etc.) and should not be used just to brand your application, or display your company name.
Update (July 31, 2015)
Google apps are now moving in the direction of having splash screens (see drive, GMail, etc.)
Additionally, You shouldn't be doing any work other than de-referencing views in the onCreate() method. Any long running operations such as retrieving information from memory (database, prefs, etc.) should be done in an AsyncTaskLoader or AsyncTask.
If you are using AppCompatActivity then create below theme in style.xml :
<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowIsTranslucent">true</item>
</style>
And in manifest file for SplashActivity add theme :
android:theme="#style/Theme.Transparent"
Add below line in your Theme of splash screen as you wrote you are using splash screen
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowIsTranslucent">true</item>
1- Make windowDisablePreview false in your style.xml
<item name="android:windowDisablePreview">false</item>
2- Add windowBackground in your style.xml.
<item name="android:windowBackground">#drawable/your_background</item>
I would like to change the whole application's text and background color in Java, is this possible? With this I mean to change the color of every item in the application (TextViews, ListView items, everything).
Is this possible?
I have tried using a custom-made style but I can't make it work. Here is the xml file (put in the res/layout/values folder):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Light">
<item name="android:textColor">#00FF00</item>
</style>
</resources>
Let's say I just want to change the text color for now.
Now I call this style in my application like this:
public void onCreate(Bundle icicle) {
Activity.setTheme(android.R.style.light);
super.onCreate(icicle);
setContentView(R.layout.main);
But I get the error light cannot be resolved or is not a field.
Update:
One way I found to do this programmatically is to restart the activity, calling
this.setTheme(R.style.Light);
onCreate(null);
However, this works only for the current activity and not for the whole application. It would be great if it were possible to do this launching another activity, not only the current one.
You're trying it in a bit to simple way. Like this you're just adjusting your general Activity's background instead of all the different Views that are out there.
In order to try and adjust every type of View (Button, TextView etc) you'll need to address all their own styles to overwrite them.
Per example if you want to adjust Button you'll need in your own general style:
<item name="android:buttonStyle">#style/ButtonHoloDark</item>
This will point at your own custom style, which takes its parent from the Android's standard Button.
<style name="ButtonHoloDark" parent="android:style/Widget.Button">
<item name="android:background">#drawable/btn_default_holo_dark</item>
<item name="android:textColor">#ffffff</item>
</style>
Be warned, doing this for every View will take you quite some themes and styles.
You can find a great example how to do this exactly in theHoloEverywhere lib, which basically does the same for creating a holo theme backported to Android 2.2 or so
Finally, drop the Activity.setTheme(android.R.style.light); stuff, and just set your own theme via the manifest.
Ok so I found one possible solution, which is to pass the theme information between the activities using intents and the putExtra method.
Code for the first activity (the caller):
Intent i = new Intent(this, ActivityToCall.class);
i.putExtra("key", R.style.Light);
startActivity(i);
Code for the second activity (the called one):
public void onCreate(Bundle icicle) {
int theme = getIntent().getIntExtra("key",-1);
this.setTheme(theme);
super.onCreate(icicle);
// other code...
I don't know if it's the best possible approach but at least it works.
The attributes you want change are:
<style name="AppTheme" parent="android:style/Theme.Holo">
<item name="android:textColorPrimary">...</item>
<item name="android:textColorSecondary">...</item>
</style>
There are a few other ways to change them: Please Refer to this information
You can then set this Theme via the Manifest or setTheme(R.style.AppTheme) in your Activity's onCreate(...)