134

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
  • 10,002
  • 6
  • 30
  • 22

9 Answers9

224

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.

tasomaniac
  • 10,234
  • 6
  • 52
  • 84
Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • Interesting that you needed the onPreDraw listener -- I thought the framework did that work for you :) – klmprt Nov 05 '14 at 20:03
  • @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. – Alex Lockwood Nov 05 '14 at 20:12
  • 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??? – Prathamesh Shetye Nov 13 '14 at 08:05
  • What would I need to do to prevent the Toolbar from animating in Approach #1? – Edward van Raak Dec 04 '14 at 16:45
  • @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: https://github.com/android/platform_frameworks_support/blob/master/v7/appcompat/res/layout/abc_screen_toolbar.xml#L37). I might be wrong though... try it out and let me know if it works. – Alex Lockwood Dec 04 '14 at 16:49
  • 14
    Any ideas why findViewById(android.R.id.navigationBarBackground) is returning null on Lollipop? I am using appcompat-v7 – radzio Feb 18 '15 at 17:25
  • @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 Feb 18 '15 at 18:17
  • @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). – Alex Lockwood Feb 18 '15 at 18:21
  • Any way to do approach #1 in transition xml? – Tony Wickham Feb 26 '15 at 03:18
  • @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). – Alex Lockwood Feb 26 '15 at 03:45
  • @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... – Tony Wickham Feb 26 '15 at 04:49
  • @TonyWickham If you want to exclude the Action Bar from the transition and you are using AppCompat, try adding `` as well (see [this G+ post](https://plus.google.com/+AlexLockwood/posts/RPtwZ5nNebb) for some other related details). – Alex Lockwood Feb 26 '15 at 04:52
  • 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. – Dima Kornilov Feb 26 '15 at 15:51
  • What is the TRANSITION_NAME for Action bar? so that i can prevent the action bar from flickering – Dinesh T A Mar 12 '15 at 13:20
  • 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. – Alex Lockwood Mar 13 '15 at 14:47
  • 3
    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. – Akshat Apr 07 '15 at 05:41
  • 16
    @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 – Noa Drach Jun 05 '15 at 13:36
  • I have the same problem with Approach #2. But Approach #1 works for me! – goRGon Jul 30 '15 at 01:49
  • 2
    @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? –  Aug 03 '15 at 21:52
  • 1
    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. – rekire Dec 22 '15 at 08:40
  • 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? – anandbibek Dec 31 '15 at 08:25
  • 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. – Greg Ennis Jan 22 '16 at 03:12
  • This doesnt seem to work when you have a layout like this https://gist.github.com/lawloretienne/ef85146dcdbd2a95b96ab796a448c782 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. – Etienne Lawlor Apr 27 '16 at 02:05
  • I have found a fix for this issue and it is in my repo https://github.com/lawloretienne/SharedElementTransition – Etienne Lawlor May 02 '16 at 04:31
  • 1
    @toobsco42 what is different in your approach, i saw ur repo and didn't find anything special about it. could you mention the point – Masoud Dadashi Jun 14 '16 at 23:18
  • 6
    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 – Boy Jun 23 '16 at 13:49
  • 8
    I am trying this solution on nexus 6 with android nougat. But both the approaches are not working for me . – Pardeep Kr Nov 21 '16 at 11:02
  • 2
    findViewById(android.R.id.navigationBarBackground) and findViewById(android.R.id.statusBarBackground) returns null for me . – Pardeep Kr Nov 21 '16 at 11:03
  • 2
    Approach #1 didn't work on Nougat for me for some reason, however approach #2 did! Thanks so much! – Piotr Zawadzki Feb 01 '17 at 13:13
  • @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. – Tim Autin Nov 29 '17 at 03:14
  • How can i prevent overlapping while performing transitions?? I have tried using: window.setAllowEnterTransitionOverlap(false); and window.setAllowReturnTransitionOverlap(false); – Sumit Shukla Aug 09 '18 at 08:48
4

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.

Community
  • 1
  • 1
Chad Schultz
  • 7,770
  • 6
  • 57
  • 96
4

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!

LukeWaggoner
  • 8,869
  • 1
  • 29
  • 28
  • Hi. Isn't this the same as Approach #1 in the top answer? – rlay3 Jan 11 '18 at 02:26
  • @rlay3 Not entirely. As far as I can tell, he never mentions the fact that this need only be set in the destination activity. – LukeWaggoner Jan 11 '18 at 16:24
  • hey @LukeWaggoner could you help me this: https://stackoverflow.com/questions/50189286/shared-element-transition-is-not-exiting-properly – blackHawk May 05 '18 at 15:50
  • You must exclude the statusbar and the navigationbar in both activity, the caller and the called. – shaithana Nov 12 '19 at 00:11
3

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.

klmprt
  • 651
  • 6
  • 7
  • Did this work for you? I tried this and the status bar and navigation bar still flashes white during the transition. – rlay3 Oct 31 '14 at 05:37
  • 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? – klmprt Oct 31 '14 at 14:34
  • @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 Nov 05 '14 at 02:22
  • @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 Nov 05 '14 at 02:27
  • @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. – Alex Lockwood Nov 05 '14 at 17:59
  • does android.R.id.window_status_bar exists for anyone? – Rafael Sanches Apr 30 '16 at 01:44
3

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);
Randeep
  • 403
  • 1
  • 4
  • 11
1

getWindow().setEnterTransition(null); on the Entering transition removed the white overlay for me.

0

Here is how I did it. I share both the Status Bar and the Navigation Bar in the SharedElementTransition along with an ImageView :

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  View imageView = view.findViewById(R.id.iv);
  Resources resources = view.getResources();
  imageView.setTransitionName(resources.getString(R.string.transition_image_thumbnail));

  Pair<View, String> p1 = Pair.create(imageView, resources.getString(R.string.transition_image_thumbnail));

  Window window = getActivity().getWindow();

  View navigationBar = getActivity().findViewById(android.R.id.navigationBarBackground);
  View statusBar = getActivity().findViewById(android.R.id.statusBarBackground);

  Pair<View, String> p2 = Pair.create(statusBar, statusBar.getTransitionName());
  Pair<View, String> p3 = Pair.create(navigationBar, navigationBar.getTransitionName());

  ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
          p1, p2, p3);

  ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
} else {
  startActivity(intent);
}
Etienne Lawlor
  • 6,817
  • 18
  • 77
  • 89
0

In case of an animation

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
            android:duration="500"
            android:fromYDelta="100%p"
            android:toYDelta="0">
    </translate> <!--Define duration here-->
</set>

I ended up making the status bar transparent in @colors

<color name="transparent">#00000000</color>

for an example theme like the following in @styles

<style name="Theme.AppCompat.Translucent" parent="Theme.MaterialComponents.Light.NoActionBar">
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowAnimationStyle">@android:style/Animation</item>
        <item name="android:statusBarColor">@color/transparent</item>
    </style>
realsarm
  • 583
  • 6
  • 11
0

If someone using androidx and find
fade.excludeTarget(android.R.id.navigationBarBackground, true);
not work, it just because this window cannot find the view with this id, you should replace with this.excludeTarget(androidx.appcompat.R.id.action_bar_container,true).

But now is Navigation time, so sad to know this until now.

wjploop
  • 209
  • 2
  • 8