Android 如何防止状态栏和导航栏在活动场景动画过渡期间出现动画?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26600263/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:56:37  来源:igfitidea点击:

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

androidandroid-5.0-lollipopshared-element-transitionactivity-transition

提问by rlay3

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.

首先,我的状态栏背景设置为深棕色,导航栏背景默认为黑色。我正在使用 Material 灯光主题。

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

我正在使用ActivityOptions.makeSceneTransitionAnimation默认转换开始一个新活动,我注意到状态栏和导航栏都短暂地淡化为白色,然后又恢复为正确的颜色。

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)

要获得转换的全部效果,您必须在调用和被调用活动上启用窗口内容转换。否则,调用 Activity 将启动退出转换,但随后您将看到窗口转换(如缩放或淡入淡出)

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

我正在getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);调用和被调用的活动中使用。

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?

如何防止状态栏和导航栏在活动场景动画过渡期间出现动画?

回答by Alex Lockwood

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

方法#1:从窗口的默认退出/进入淡入淡出过渡中排除状态栏和导航栏

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 Fadetransition. Simply add the following code to your Activitys' onCreate()methods:

导航/状态栏在过渡期间淡入和淡出的原因是因为默认情况下,一旦过渡开始,所有非共享视图(包括导航/状态栏背景)将分别在您的调用/被调用活动中淡入/淡入. 但是,您可以通过从窗口的默认退出/进入Fade转换中排除导航/状态栏背景来轻松解决此问题。只需将以下代码添加到您的活动的onCreate()方法中:

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

也可以使用 XML 在活动的主题中声明此转换(即在您自己的res/transition/window_fade.xml文件中):

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

方法#2:将状态栏和导航栏添加为共享元素

This approach builds off of klmprt's answer, which almostworked for me... although I still needed to make a couple of modifications.

这种方法建立在 klmprt 的答案之上,这几乎对我有用……尽管我仍然需要进行一些修改。

In my calling Activity, I used the following code to start the Activity:

在我的调用 Activity 中,我使用以下代码来启动 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:

到目前为止,这与 klmprt 在他的回答中建议的内容基本相同。但是,我还需要在我调用的 ActivityonCreate()方法中添加以下代码,以防止状态栏和导航栏在过渡期间“闪烁”:

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

添加状态栏和导航栏背景作为共享元素将强制它们绘制在窗口默认退出/进入淡入淡出过渡的顶部,这意味着它们不会在过渡期间淡出。有关此方法的更多讨论可以在此 Google+ 帖子 中找到。

回答by Chad Schultz

Completely prevent Activity transitions from interfering with shared element transitions:

完全防止Activity过渡干扰共享元素过渡:

On the exiting activity, call getWindow().setExitTransition(null);

On the entering activity, call getWindow().setEnterTransition(null);

在退出活动上,调用 getWindow().setExitTransition(null);

在进入活动中,调用 getWindow().setEnterTransition(null);

From https://stackoverflow.com/a/34907685/967131

来自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 answerand 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.

我从Alex Lockwood 的回答开始,并进行了大量实验以使其正常工作。它的核心是正确的,虽然我不需要他为接收Activity建议的代码,但是通过在Fragment(而不是Activity)中调用它并将工具栏设置为操作栏,我遇到了一些问题。

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.

哦,碎片的东西?我看到很多评论试图检索对状态栏和导航栏的引用都是空的。同样的事情也发生在我身上,直到我意识到我不会在 Fragment 的布局中找到那些……它们高于那个级别。因此,下面的代码从活动中获取装饰视图并进行搜索。然后我发现他们没有问题。

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

注意R.string.transition_appbarlayoutR.id.appbarlayout。这些 ID 是任意的,只要它们与您的代码使用的内容相匹配。在我的 XML 中,我像这样布置自定义操作栏(已编辑为基本要素):

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

然后你会像这样在你的 Fragment 中调用它:

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.

使用您想要的任何值,只要它与您的 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.

这可以防止状态栏、工具栏和导航栏(返回/主页/最近使用的应用程序按钮)在过渡期间闪烁。Activity过渡的其余部分是正常的。

In my case, our app theme has a android:windowBackgroundof 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.

就我而言,我们的应用程序主题android:windowBackground是蓝色的。这会导致过渡中出现蓝色闪光,这非常令人沮丧。但是,与其做出影响整个应用程序的更改,现在我将采用第一个,快速且肮脏的选项。

回答by Randeep

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:

据我了解,这是由活动转换重叠引起的。为了克服这个问题,我在这onCreate()两个活动的方法中使用了以下值:

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);

回答by LukeWaggoner

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.

我刚刚遇到了同样的问题,答案似乎缺少了拼图的关键部分。请记住,在共享元素转换中,一切都发生在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!

这应该可以解决您的问题!

回答by klmprt

You need to share them in ActivityOptions.makeSceneTransitionAnimation.

你需要分享它们 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)

(请原谅伪娘;我手头没有确切的 android.R.id 值)

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

您可以在共享视图后运行适当的转换。

回答by Dominic Davies

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

getWindow().setEnterTransition(null);在 Entering 过渡上为我删除了白色覆盖层。

回答by toobsco42

Here is how I did it. I share both the Status Barand the Navigation Barin the SharedElementTransitionalong with an ImageView:

这是我如何做到的。我同时分享 theStatus Bar和 the Navigation BarinSharedElementTransition以及一个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);
}