How do I prevent the status bar and navigation bar from animating during an activity scene animation transition?

48,618

Solution 1

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:

@Override
protected 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.

Solution 2

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.

Solution 3

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!

Solution 4

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);

Solution 5

You need to share them in ActivityOptions.makeSceneTransitionAnimation.

E.g:

ActivityOptions.makeSceneTransitionAnimation(... Pair.create(activity.findViewById(android.R.id.window_status_bar), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME) 

(excuse the psuedo; I don't have the exact android.R.id value on hand)

You can run an appropriate transition after having shared the views.

Share:
48,618
rlay3
Author by

rlay3

Updated on April 20, 2021

Comments

  • rlay3
    rlay3 about 3 years

    Firstly, my status bar background is set to dark brown, and my navigation bar background is default black. I'm using the Material light theme.

    I'm starting a new activity using ActivityOptions.makeSceneTransitionAnimation with default transitions, and I notice that both the status and navigation bars briefly fade to white and then back to the correct colors.

    According to the documentation:

    To get the full effect of a transition, you must enable window content transitions on both the calling and called activities. Otherwise, the calling activity will start the exit transition, but then you'll see a window transition (like scale or fade)

    I am using getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); on both the calling and called activities.

    Similarly, if I change the enter transition to a slide, both the status and navigation bars briefly have a slide transition with a white background.

    How do I prevent the status bar and navigation bar from animating during an activity scene animation transition?

  • rlay3
    rlay3 over 9 years
    Did this work for you? I tried this and the status bar and navigation bar still flashes white during the transition.
  • klmprt
    klmprt over 9 years
    Yes, it worked for me. Are you sure there isn't another view that might be overlapping them temporarily? Have you tried setting up a simple activity transition in a 'sandboxed' environment to isolate the issue?
  • Alex Lockwood
    Alex Lockwood over 9 years
    @klmprt I think you also need to postpone the enter transition in order to get it working... I also wasn't able to prevent the status/navigation bars from animating simply by sharing the views. I guess you need to wait for the window's decor view to finish its layout before you let the enter transition begin.
  • Alex Lockwood
    Alex Lockwood over 9 years
    @klmprt What my answer still doesn't address though is how to prevent the Action Bar's background color from animating during the transition. If both Activity's share the same Action Bar background color, then the action bar's background color will appear to animate as the calling Activity's action bar gradually fades out and the called Activity's action bar gently fades in. Do you have any idea how to get around this issue?
  • Alex Lockwood
    Alex Lockwood over 9 years
    @klmprt Actually, I think there is an even better solution. You can simply exclude the navigation/status bar backgrounds as targets in the window's default exit/enter fade transition. See my updated answer for details.
  • klmprt
    klmprt over 9 years
    Interesting that you needed the onPreDraw listener -- I thought the framework did that work for you :)
  • Alex Lockwood
    Alex Lockwood over 9 years
    @klmprt It looks like the framework should do that work for you according to the source code... but for some reason it was necessary in order to get the desired effect.
  • Prathamesh Shetye
    Prathamesh Shetye over 9 years
    tried this, doesn't work! I wanted the transitions akin to the transitions achieved by using android:transitionName in your layout XMLs... any idea how thats possible???
  • Edward van Raak
    Edward van Raak over 9 years
    What would I need to do to prevent the Toolbar from animating in Approach #1?
  • Alex Lockwood
    Alex Lockwood over 9 years
    @EdwardvanRaak I haven't tested this myself, so I could be wrong. But at first glance it looks like the ID is named R.id.action_bar in appcompat-v7 (see the source code here: github.com/android/platform_frameworks_support/blob/master/v‌​7/…). I might be wrong though... try it out and let me know if it works.
  • radzio
    radzio over 9 years
    Any ideas why findViewById(android.R.id.navigationBarBackground) is returning null on Lollipop? I am using appcompat-v7
  • Alex Lockwood
    Alex Lockwood over 9 years
    @radzio Are you calling it after you call setContentView() on the activity? Does findViewById(android.R.id.statusBarBackground) return null as well? Does it return null for both the called and calling activities during the transition?
  • Alex Lockwood
    Alex Lockwood over 9 years
    @radzio Not sure then. But if approach #2 doesn't work for you because it is returning null, you could always try approach #1 as well (which is probably the easier solution anyway).
  • Tony Wickham
    Tony Wickham over 9 years
    Any way to do approach #1 in transition xml?
  • Alex Lockwood
    Alex Lockwood over 9 years
    @TonyWickham I edited my post to include the XML as well. Just set it as the android:window{Exit,Enter}Transition in the activity's theme. (BTW, let me know if it works for you work... I didn't test it myself and wrote it off the top of my head. :P I'm pretty sure it will work though).
  • Tony Wickham
    Tony Wickham over 9 years
    @Alex It doesn't stop the action bar from moving, but it does exclude/include other id's so I'm not sure what's wrong...
  • Alex Lockwood
    Alex Lockwood over 9 years
    @TonyWickham If you want to exclude the Action Bar from the transition and you are using AppCompat, try adding <target android:excludeId="@id/action_bar_container"/> as well (see this G+ post for some other related details).
  • Dima Kornilov
    Dima Kornilov over 9 years
    What do you think about filing a bug against Android? The framework should always take care of sharing status bar and navigation bar through activity transition. I can't imagine a case when it shouldn't.
  • Dinesh T A
    Dinesh T A over 9 years
    What is the TRANSITION_NAME for Action bar? so that i can prevent the action bar from flickering
  • Alex Lockwood
    Alex Lockwood over 9 years
    I don't think it has a transition name. But if you are using AppCompat v7 you can reference the action bar's ID instead (R.id.action_bar_container). Or if you are using a Toolbar instead of the action bar, you can give it whatever ID/transition name you want.
  • Akshat
    Akshat about 9 years
    Approach #2 works, but there is still a flicker (in the activity) when the transition happens. the status bar and navigation bar do not flickr anymore.
  • Noa Drach
    Noa Drach about 9 years
    @alex This worked almost perfectly for me until we switched to the new support library with android.support.design.widget.TabLayout and android.support.design.widget.AppBarLayout - now the flickering is back
  • goRGon
    goRGon almost 9 years
    I have the same problem with Approach #2. But Approach #1 works for me!
  • Admin
    Admin almost 9 years
    @AlexLockwood I'm having the same issue as radzio, findViewById(android.R.id.statusBarBackground) returns null for both Activities. I've tried every combination of solutions, and nothing works exactly how I'd like it to. Adding the toolbar and navigationbar to the transition works perfectly however. It's just that my view is still underneath the statusbar. Is there any other way to get a reference to the statusbar?
  • rekire
    rekire over 8 years
    I guess I know why statusBar is null in my case. I'm using the DrawerLayout which just paints that area, so there is no view to move... I'll check if I can take the DrawerLayout itself. Well that causes a funny bug where the hole layout will been pushed down with the statusbar height.
  • anandbibek
    anandbibek over 8 years
    Can't we do this using fragments in both activities like "ActA+FragA -> ActB+FragB"? If not, how do you suggest we keep this animations on single pane layout, and also have dual pane layout for tablets?
  • Greg Ennis
    Greg Ennis over 8 years
    Why did google think that it was a good idea that the default behavior cause the status and navigation bar to flash? This is really strange stuff.
  • Etienne Lawlor
    Etienne Lawlor about 8 years
    This doesnt seem to work when you have a layout like this gist.github.com/lawloretienne/ef85146dcdbd2a95b96ab796a448c7‌​82 I have removed all the attributes so that you can get the gist of what viewgroups are in the layout. This layout being the details screen or second screen you want to transition to.
  • Rafael Sanches
    Rafael Sanches about 8 years
    does android.R.id.window_status_bar exists for anyone?
  • Etienne Lawlor
    Etienne Lawlor about 8 years
    I have found a fix for this issue and it is in my repo github.com/lawloretienne/SharedElementTransition
  • Masoud Dadashi
    Masoud Dadashi about 8 years
    @toobsco42 what is different in your approach, i saw ur repo and didn't find anything special about it. could you mention the point
  • Boy
    Boy about 8 years
    android.R.id.navigationBarBackground can give you a NPE on Samsung or HTC devices as they don't have an on screen navigation bar. Do a null check before adding them as shared elements
  • Pardeep Kr
    Pardeep Kr over 7 years
    I am trying this solution on nexus 6 with android nougat. But both the approaches are not working for me .
  • Pardeep Kr
    Pardeep Kr over 7 years
    findViewById(android.R.id.navigationBarBackground) and findViewById(android.R.id.statusBarBackground) returns null for me .
  • Piotr Zawadzki
    Piotr Zawadzki over 7 years
    Approach #1 didn't work on Nougat for me for some reason, however approach #2 did! Thanks so much!
  • Tim Autin
    Tim Autin over 6 years
    @AlexLockwood With an immersive target activity (with a translucent status bar), the status bar still flashes. It works if its opaque, but if translucent, the alpha animation makes it flashes briefly. Do you have a solution? Looks like the status bar background is white whatever is set, while the nav bar works well.
  • rlay3
    rlay3 over 6 years
    Hi. Isn't this the same as Approach #1 in the top answer?
  • LukeWaggoner
    LukeWaggoner over 6 years
    @rlay3 Not entirely. As far as I can tell, he never mentions the fact that this need only be set in the destination activity.
  • blackHawk
    blackHawk about 6 years
    hey @LukeWaggoner could you help me this: stackoverflow.com/questions/50189286/…
  • Sumit Shukla
    Sumit Shukla almost 6 years
    How can i prevent overlapping while performing transitions?? I have tried using: window.setAllowEnterTransitionOverlap(false); and window.setAllowReturnTransitionOverlap(false);
  • shaithana
    shaithana over 4 years
    You must exclude the statusbar and the navigationbar in both activity, the caller and the called.