How do I prevent the status bar and navigation bar from animating during an activity scene animation transition?
There are two approaches you can use that I know of to prevent the navigation/status bar from animating during the transition:
Approach #1: Exclude the status bar and navigation bar from the window's default exit/enter fade transition
The reason why the navigation/status bar are fading in and out during the transition is because by default all non-shared views (including the navigation/status bar backgrounds) will fade out/in in your calling/called Activitys respectively once the transition begins. You can, however, easily get around this by excluding the navigation/status bar backgrounds from the window's default exit/enter Fade
transition. Simply add the following code to your Activitys' onCreate()
methods:
Transition fade = new Fade();fade.excludeTarget(android.R.id.statusBarBackground, true);fade.excludeTarget(android.R.id.navigationBarBackground, true);getWindow().setExitTransition(fade);getWindow().setEnterTransition(fade);
This transition could also be declared in the activity's theme using XML (i.e. in your own res/transition/window_fade.xml
file):
<?xml version="1.0" encoding="utf-8"?><fade xmlns:android="http://schemas.android.com/apk/res/android"> <targets> <target android:excludeId="@android:id/statusBarBackground"/> <target android:excludeId="@android:id/navigationBarBackground"/> </targets></fade>
Approach #2: Add the status bar and navigation bar as shared elements
This approach builds off of klmprt's answer, which almost worked for me... although I still needed to make a couple of modifications.
In my calling Activity, I used the following code to start the Activity:
View statusBar = findViewById(android.R.id.statusBarBackground);View navigationBar = findViewById(android.R.id.navigationBarBackground);List<Pair<View, String>> pairs = new ArrayList<>();if (statusBar != null) { pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));}if (navigationBar != null) { pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));}pairs.add(Pair.create(mSharedElement, mSharedElement.getTransitionName()));Bundle options = ActivityOptions.makeSceneTransitionAnimation(activity, pairs.toArray(new Pair[pairs.size()])).toBundle();startActivity(new Intent(context, NextActivity.class), options);
So far this is essentially the same thing that klmprt suggested in his answer. However, I also needed to add the following code in my called Activity's onCreate()
method in order to prevent the status bar and navigation bar from "blinking" during the transition:
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_next); // Postpone the transition until the window's decor view has // finished its layout. postponeEnterTransition(); final View decor = getWindow().getDecorView(); decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { decor.getViewTreeObserver().removeOnPreDrawListener(this); startPostponedEnterTransition(); return true; } });}
Adding the status bar and navigation bar backgrounds as shared elements will force them to be drawn on top of the window's default exit/enter fade transition, meaning that they will not fade during the transition. More discussion about this approach can be found in this Google+ post.
Completely prevent Activity transitions from interfering with shared element transitions:
On the exiting activity, call getWindow().setExitTransition(null);
On the entering activity, call getWindow().setEnterTransition(null);
From https://stackoverflow.com/a/34907685/967131
I suspect this may have side effects, but don't know for sure. It is dead simple and works though.
Prevent specific elements from blinking:
I started with Alex Lockwood's answer and did a fair bit of experimentation to try to get it working. The core of it is correct, although I didn't need the code he suggests for the receiving Activity, but I ran into some problems by calling it in a Fragment (instead of an Activity) and by setting a toolbar as the action bar.
Oh, the Fragment thing? I saw a lot of comments that trying to retrieve references to the status bar and navigation bar were null. The same thing happened to me as well, until I realized I wouldn't find those in the Fragment's layout... they were above that level. Hence, the code below to get the decor view from the Activity and search that. Then I found them with no problem.
In the end, I developed this utility method:
public static Bundle transitionOptions(Activity activity, int transitionViewResId, int transitionNameResId) { if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { return null; } View decorView = activity.getWindow().getDecorView(); View statusBar = decorView.findViewById(android.R.id.statusBarBackground); View navigationBar = decorView.findViewById(android.R.id.navigationBarBackground); View appBarLayout = decorView.findViewById(**R.id.appbarlayout**); View transitionView = decorView.findViewById(transitionViewResId); String transitionName = activity.getString(transitionNameResId); List<Pair<View, String>> pairs = new ArrayList<>(); pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME)); pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME)); if (appBarLayout != null) { pairs.add(Pair.create(appBarLayout, activity.getString(**R.string.transition_appbarlayout**))); } pairs.add(Pair.create(transitionView, transitionName)); //noinspection unchecked - we're not worried about the "unchecked" conversion of List<Pair> to Pair[] here return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs.toArray(new Pair[pairs.size()])) .toBundle();}
Note R.string.transition_appbarlayout and R.id.appbarlayout. These IDs are arbitrary, as long as they match what your code uses. In my XML, I layout the custom action bar like so (edited down to the essentials):
<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.AppBarLayout android:id="**@+id/appbarlayout**" android:transitionName="**@string/transition_appbarlayout**"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar"/></android.support.design.widget.AppBarLayout>
If you don't use a Toolbar like this, that part can be removed from the utility method.
Then you would call it in your Fragment like so:
startActivity(intent, UIUtils.transitionOptions(getActivity(), R.id.**my_view**, R.string.**transition_my_view**));
Using whatever values you want, as long as it matches your XML.
This prevents the status bar, the tool bar and the navigation bar (back / home / recent apps buttons) from flashing during the transition. The rest of the Activity transition is normal.
In my case, our app theme has a android:windowBackground
of blue. This causes a blue flash in the transition, which is quite frustrating. But rather than make a change that affects the entire app like that, for now I am going with the first, quick and dirty option.
I just had this same issue, and the answers appear to be missing a critical piece to the puzzle. Remember that on a shared element transition, everything happens in the Destination Activity.
In order to remove the flashing effect, simply add the following to the activity being called:
Fade fade = new Fade();fade.excludeTarget(android.R.id.statusBarBackground, true);fade.excludeTarget(android.R.id.navigationBarBackground, true);getWindow().setEnterTransition(fade);getWindow().setExitTransition(fade);
This should solve your problem!