133

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

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

根据文档

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

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);在呼叫和被叫活动中都使用。

同样,如果我将输入转换更改为幻灯片,状态栏和导航栏都会短暂地具有白色背景的幻灯片转换。

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

4

9 回答 9

224

我知道有两种方法可以用来防止导航/状态栏在过渡期间出现动画:

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

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

Transition fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setExitTransition(fade);
getWindow().setEnterTransition(fade);

也可以使用 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>

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

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

在调用 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);

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

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

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

于 2014-11-05T02:19:39.650 回答
4

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

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

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

来自https://stackoverflow.com/a/34907685/967131

我怀疑这可能有副作用,但不确定。它非常简单并且可以工作。

防止特定元素闪烁:

我从亚历克斯洛克伍德的回答开始,做了一些实验来尝试让它发挥作用。它的核心是正确的,虽然我不需要他为接收活动建议的代码,但我通过在片段(而不是活动)中调用它并将工具栏设置为操作栏遇到了一些问题。

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

最后,我开发了这个实用方法:

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

注意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>

如果您不使用这样的工具栏,则可以从实用程序方法中删除该部分。

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

startActivity(intent, UIUtils.transitionOptions(getActivity(),
                        R.id.**my_view**,
                        R.string.**transition_my_view**));

使用您想要的任何值,只要它与您的 XML 匹配。

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

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

于 2017-02-18T04:39:53.727 回答
4

我刚刚遇到了同样的问题,答案似乎缺少了难题的关键部分。请记住,在共享元素转换中,一切都发生在Destination Activity中。

为了消除闪烁效果,只需将以下内容添加到被调用的活动中:

Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);

getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);

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

于 2018-01-10T17:08:56.077 回答
3

你需要分享它们ActivityOptions.makeSceneTransitionAnimation.

例如:

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

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

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

于 2014-10-31T01:22:23.620 回答
3

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

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
于 2017-07-12T14:42:47.027 回答
1

getWindow().setEnterTransition(null);在进入过渡时为我删除了白色覆盖。

于 2016-08-28T15:50:27.767 回答
0

如果有人使用 androidx 并且 find
fade.excludeTarget(android.R.id.navigationBarBackground, true);
不起作用,那只是因为此窗口找不到具有此 id 的视图,您应该替换为 this.excludeTarget(androidx.appcompat.R.id.action_bar_container,true).

但是现在是导航时间,直到现在才知道这一点很难过。

于 2021-04-19T18:28:07.950 回答
0

如果是动画

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

我最终使状态栏在@colors

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

对于如下示例主题@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>
于 2020-12-24T06:13:28.350 回答
0

这是我的做法。我同时分享 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);
}
于 2016-07-20T00:23:20.323 回答