Is it possible to disable the white flash which appears during Shared Element Transitions? I have a dark theme in my Android app, and the white flash makes the app unpleasant to use whenever there's a transition.
While there were some questions regarding this issue, I couldn't solve this problem in my app so far.
(e.g. the cause for the blinking was in this stackoverflow question a NavigationDrawerLayout, but my transition gets started in my Activity after a view been clicked in a Fragment)
styles.xml
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
This method starts a new transition in my Activity
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void startItemDetailActivityTransition(Item pItem, boolean pForceComments, View pTransitionView) {
Intent intent = new Intent(this,ItemDetailActivity.class);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(pTransitionView, getString(R.string.transition_item)));
intent.putExtra(ItemDetailActivity.EXTRA_ITEM, pItem);
intent.putExtra(ItemDetailActivity.EXTRA_FORCE_COMMENTS, pForceComments);
startActivity(intent, options.toBundle());
}
Thanks for the help
try excluding the parts which have the white flash like this
Fade fade = new Fade();
fade.excludeTarget(R.id.p1,true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setEnterTransition(fade);
}
Related
The Problem
I have an Activity called Activity A which has the following transitions defined.
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void slideAnimation(Window window) {
//runs when the activity is being entered
//runs in reverse when the activity is being destroyed
Slide slideEnter = new Slide();
slideEnter.setDuration(mShortAnimationDuration);
slideEnter.excludeTarget(android.R.id.statusBarBackground, true);
slideEnter.excludeTarget(R.id.app_bar_layout, false);
window.setEnterTransition(slideEnter);
//runs when the calling activity is exiting
// runs in reverse when the activity is reentering
Slide slideExit = new Slide();
slideExit.setDuration(mShortAnimationDuration);
slideExit.excludeTarget(android.R.id.statusBarBackground, true);
slideExit.excludeTarget(R.id.app_bar_layout, false);
window.setExitTransition(slideExit);
}
When I invoke Activity A everything works normal. The statusbar remains where it is but the appbar is animated into the screen. However when I run Activity B via A , the slideexit transition doesn't honor the statusbar. The status bar goes away however the appbar remains fixed (it is properly excluded from the animation).
What I have tried
If I comment out the code for slideexit and only keep the enter transition in A, the statusbar remains fixed during transition from A to B. This proves that the issue comes before B has been invoked. The issue is with the exittransition of A.
A's exit transition doesn't exclude the status bar and takes it off screen. B merely slides it back.
This is my styles.xml
<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>
<item name="android:windowContentTransitions">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
</style>
This is how I invoke my activity
void transitionTo(Intent i) {
ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(this), null);
getActivity(this).startActivity(i, transitionActivityOptions.toBundle());
}
I am using nouget 7.0 on an HTC 10 with the support library version 24.2.1.
I strongly believe that it is some framework issue as I clearly remember this working last year maybe 3 months ago or so.
Or am I doing something wrong?
WHAT I HAVE
I have an app with dynamic theming support. Everything works very well, but now while changing the themes I want to give it a fade-in and fade-out effect to make the theme changing transition look seamless.
WHAT I HAV TRIED
1) I have tried to recreate() the activity, but that doesn't apply any animations.
2) I have added a window animations, like this,
<style name="WindowFadeTransition">
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="WindowAnimationTransition">
<item name="android:windowEnterAnimation">#android:anim/fade_in</item>
<item name="android:windowExitAnimation">#android:anim/fade_out</item>
</style>
And I have applied the style in my activity.
I re-open the activity using,
Intent intent = new Intent(getActivity(), SettingsThemeActivity.class);
getActivity().startActivity(intent);
getActivity().finish();
The animation works really well, but there is a problem.
THE BUG
As I have applied the window animation to the activity itself, whenever I open and close the activity, the animation triggers, which is not what I want. I want the animation to work only when I am changing theme and calling the above lines of code to re-open the activity.
I know it's kind of tricky. How to fix this issue? Any ideas?
Try this After startActivity(); call overridePendingTransition(R.anim.fade_in,R.anim.fade_out);
I have an activity with a DrawerLayout but whenever it opens there is a delay like a split-second where the screen is white then my screen is drawn.
This happens after the Transition finishes. So it sort of looks like the screen animation transition is jumping.
I tried putting this on my OnCreate after binding the views with ButterKnife but it did nothing.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
postponeEnterTransition();
drawerLayout.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onPreDraw() {
drawerLayout.getViewTreeObserver().removeOnPreDrawListener(this);
startPostponedEnterTransition();
return true;
}
});
}
Yes I am optimizing it for Lollipop, and for pre-Lollipop devices I am jsut using overridePendingTransitionsand it works fine. My problem is only on Lollipop devices.
Btw, my Enter and Exit transitions are both fade_in_outdefined in xml and specified in styles
<style name="AppTheme" parent="Theme.AppCompat.Light">
<item name="colorAccent">#color/pink</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowContentTransitions">true</item>
<!-- specify enter and exit transitions -->
<!-- options are: explode, slide, fade -->
<item name="android:windowEnterTransition">#transition/fade_in_out_transition</item>
<item name="android:windowExitTransition">#transition/fade_in_out_transition</item>
<!-- specify shared element transitions -->
<item name="android:windowSharedElementEnterTransition">#transition/change_clip_bounds</item>
<item name="android:windowSharedElementExitTransition">#transition/change_clip_bounds</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden|adjustResize</item>
</style>
I finally found a solution to this. I don't know why or how it worked out but I just know that it removed the delay in the animations. I added a handler in the OnCreate of the activity that would run the other statements for setting up, i.e. adding the initial fragment into view, after 300ms
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
switchFragment();
}
}, 300);
Maybe its because lollipop has default layoutTransition on UI elements, have you tried?
drawerLayout.setLayoutTransition(null)
I would change your exit transition:
item name="android:windowExitTransition">#transition/fade_in_out_transition</item>
item
To window return:
name="android:windowReturnTransition">#transition/fade_in_out_transition</item>
When you are using window exit, the visibility of the window is changed to invisible briefly before your next transition starts.
Sets the Transition that will be used to move Views out of the scene
when the fragment is removed, hidden, or detached when not popping
the back stack. The exiting Views will be those that are regular
Views or ViewGroups that have isTransitionGroup() return true.
Typical Transitions will extend Visibility as exiting is governed by
changing visibility from VISIBLE to INVISIBLE. If transition is null,
the views will remain unaffected.
setExitTransition
Returning the transition handles the window closing, as opposed to exiting and does not affect the window visibility.
Reference to a Transition XML resource defining the desired
Transition used to move Views out of the scene when the Window is
preparing to close. Corresponds to
setReturnTransition(android.transition.Transition).
android:windowReturnTransition
I would also recommend using reenter to manage back presses.
Reference to a Transition XML resource defining the desired Transition
used to move Views in to the scene when returning from a
previously-started Activity. Corresponds to
setReenterTransition(android.transition.Transition).
android:windowReenterTransition
Understanding exit/reenter shared element transitions
You can also set a bool value that will allow the transitions to overlap, however the overlap may be too long
for what you want.
setAllowEnterTransitionOverlap(boolean)
Also I'd upgrade the lollipop to 5.0.1
There are bugs in 5.0.0 that have been fixed in 5.0.1
This blog by Linton Ye covers in detail the issues surrounding Lollipop transitions and bugs.
My Journey to Lollipop Transitions: part 1
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.
I implemented an image transition between two activities using the new shared elements from lollipop. It's working but I get a weird white blinking on the entire screen during the transition and I can't find how to get rid of it. Here is an example:
Here is how the second activity is launched
public static void launch(
#NonNull Activity activity, #NonNull View transitionView, Game game) {
ActivityOptionsCompat options =
ActivityOptionsCompat.makeSceneTransitionAnimation(
activity, transitionView, game.gameFullId);
Intent intent = new Intent(activity, ListImportationLoginActivity.class);
intent.putExtra(INTENT_EXTRA_GAME, retailer);
ActivityCompat.startActivity(activity, intent, options.toBundle());
}
Then in onCreate:
ViewCompat.setTransitionName(mLogoView, mGame.gameFullId);
And the theme file:
<resources>
<style name="Theme.MyApp.NoActionBar" parent="Theme.MyApp.NoActionBar.Base">
<item name="android:windowContentTransitions">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowSharedElementEnterTransition">#android:transition/move</item>
<item name="android:windowSharedElementExitTransition">#android:transition/move</item>
</style>
</resources>
Thanks for your help
On the exiting activity, call
getWindow().setExitTransition(null);
On the entering activity, call
getWindow().setEnterTransition(null);
It will prevent the fade out of the exiting activity and the fade in of the entering activity, which removes the apparent blinking effect.
I solved this issue by changing background color of my default theme, hope this is still can help to someone save the time.
<item name="android:windowBackground">#color/black</item>
<item name="android:colorBackground">#color/black</item>
The "white blinking" you are seeing is the result of the two activities alpha-animating in and out during the transition: when activity A starts activity B, activity A fades out and activity B fades in.
If you want to prevent the status bar and/or navigation bar from fading during the transition (and thus reducing the "blinking" effect a bit), you can look at this post.
Make some method in helper like
public static Transition makeEnterTransition() {
Transition fade = new Fade();
fade.excludeTarget(android.R.id.navigationBarBackground, true);
fade.excludeTarget(android.R.id.statusBarBackground, true);
return fade;
}
Execute it in the activity that you are starting like this
getWindow().setEnterTransition(TransitionUtils.makeEnterTransition());
Source
https://github.com/alexjlockwood/custom-lollipop-transitions/
I have had similar blinking issues and tried many of the examples mentioned here but for me it didn't solve the issues. What did work for me was changing the window background for the second activity theme to transparent. (#Webdma used black, but in my case that made the screen flash black instead of white)
<item name="android:windowBackground">#android:color/transparent</item>
<!-- edit in your theme -->
<item name="android:windowEnterTransition">#android:transition/no_transition</item>
<item name="android:windowExitTransition">#android:transition/no_transition</item>
I had a similar problem.
I solved the blinking status bar and navigation bar issues by excluding them from the transition as per #Alex's suggestion, but the screen was still blinking when switching between the activities. When I removed the
"finish();" statement after startActivity(); the screen stopped blinking.
May it was due to the closing of calling activity.
Hope this helps someone.
Some useful answers above.
As far as I understand this is caused by activity transition overlap. To overcome this issue I have used the following values in the onCreate() methods of both activities:
getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
Add this in your style.xml. This prevents the screen from Blinking
<item name="android:windowIsTranslucent">true</item>
In my situation, the second activity did not have a status bar which was defined in the activity theme with this tag.
<item name="android:windowFullscreen">true</item>
Since it was not mandatory to hide the status bar in portrait mode, I removed this tag and manually hide/show the status bar when needed and the blinking is gone.
Add these codes inside onCreate of both Activities where you doing Transition elements
Fade fade = new Fade();
View decor = getWindow().getDecorView();
fade.excludeTarget(decor.findViewById(R.id.action_bar_container),true);
fade.excludeTarget(android.R.id.statusBarBackground,true);
fade.excludeTarget(android.R.id.navigationBarBackground,true);
getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);
This will exclude the animation from the navigation and status bar, So no more blinking
Elements fade in and out, unless you specify explicitly they are the same on both activities. That includes status and navigation bar.
In your particular case, I would add the toolbar and these two views to the shared elements list:
List<Pair> viewPairs = new ArrayList<>();
viewPairs.add(Pair.create(findViewById(android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
viewPairs.add(Pair.create(findViewById(android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
// Add your views...
Pair[] array = new Pair[viewPairs.size()];
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), viewPairs.toArray(array)).toBundle();
// ...
ActivityCompat.startActivity(activity, intent, options.toBundle());
In Java, add the below line in the parent activity after ActivityCompat.startActivity(activity, intent, options.toBundle());
getWindow().setExitTransition(null);
and add the below line in onCreate method of child activity
getWindow().setEnterTransition(null);
In Kotlin, add the below line in the parent activity
window.setExitTransition = null
and add the below line in onCreate method of child activity
window.setEnterTransition = null