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

匿名 (未验证) 提交于 2019-12-03 01:27:01

问题:

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?

回答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):

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> 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.



回答2:

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.



回答3:

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

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.



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


回答5:

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!



回答6:

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 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 p2 = Pair.create(statusBar, statusBar.getTransitionName());   Pair p3 = Pair.create(navigationBar, navigationBar.getTransitionName());    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),           p1, p2, p3);    ActivityCompat.startActivity(getActivity(), intent, options.toBundle()); } else {   startActivity(intent); } 


回答7:

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



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!