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


127

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

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

根据文档

要获得过渡的全部效果,必须在调用和被调用活动上都启用窗口内容过渡。否则,呼叫活动将开始退出过渡,但随后您将看到一个窗口过渡(例如缩放或淡入淡出)

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);同时在调用和被调用活动上使用。

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

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

Answers:


217

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

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

过渡期间导航/状态栏逐渐淡入和淡出的原因是,默认情况下,过渡开始后,所有非共享视图(包括导航/状态栏背景)将分别在您的呼叫/被叫活动中淡出/进入。但是,您可以通过从窗口的默认退出/进入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);

也可以使用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在他的回答中建议的相同。但是,我还需要在我的“活动” 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+帖子中找到有关此方法的更多讨论。


14
有什么想法为什么findViewById(android.R.id.navigationBarBackground)在棒棒糖上返回null?我正在使用appcompat-v7
radzio

3
方法2可以使用,但是在过渡发生时(活动中)仍然存在闪烁。状态栏和导航栏不再闪烁。
阿克斯哈特2015年

16
@alex这对我来说几乎是完美的,直到我们使用android.support.design.widget.TabLayoutandroid.support.design.widget.AppBarLayout切换到新的支持库-现在闪烁起来了
Noa Drach

6
android.R.id.navigationBarBackground可以在三星或HTC设备上为您提供NPE,因为它们没有屏幕上的导航栏。在将它们添加为共享元素之前进行空检查
Boy

8
我正在使用Android Nougat在Nexus 6上尝试此解决方案。但是这两种方法都不适合我。
Pardeep Kr

4

完全防止活动转换干扰共享元素转换:

在退出活动中,调用getWindow()。setExitTransition(null);

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

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

我怀疑这可能有副作用,但不确定。它简直是简单而有效。

防止特定元素闪烁:

我从亚历克斯·洛克伍德的答案开始并做了相当多的尝试以使其正常工作。它的核心是正确的,尽管我不需要他为接收活动所建议的代码,但是我遇到了一些问题,方法是在Fragment(而不是Activity)中调用它,并将工具栏设置为动作栏。

哦,碎片的东西?我看到很多评论,试图检索对状态栏和导航栏的引用为空。同样的事情也发生在我身上,直到我意识到我不会在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相匹配即可。

这样可以防止状态栏,工具栏和导航栏(“后退” /“主页” /“最近使用的应用程序”按钮)在过渡期间闪烁。活动过渡的其余部分是正常的。

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


3

您需要与他们分享 ActivityOptions.makeSceneTransitionAnimation.

例如:

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

(对不起,伪装;我手边没有确切的android.R.id值)

共享视图后,您可以运行适当的过渡。


它能为您提供帮助吗?我尝试了此操作,过渡期间状态栏和导航栏仍然闪烁白色。
rlay3

是的,它对我有用。您确定没有其他视图可能会暂时重叠它们吗?您是否尝试过在“沙盒”环境中设置简单的活动过渡以隔离问题?
klmprt 2014年

@klmprt我认为您还需要推迟enter转换才能使其正常工作...我也无法仅通过共享视图来阻止状态/导航栏动画。我猜您需要先等待窗口的装饰视图完成布局,然后才能开始输入转换。
亚历克斯·洛克伍德2014年

@klmprt我的答案仍然没有解决,是如何防止在过渡期间使操作栏的背景色动起来。如果两个Activity共享相同的Action Bar背景色,则随着调用的Activity的Action Bar逐渐淡出并且被调用的Activity的Action Bar逐渐淡入,该Action Bar的背景色将呈现动画效果。您知道如何解决此问题问题?
亚历克斯·洛克伍德2014年

@klmprt实际上,我认为有一个更好的解决方案。您可以在窗口的默认退出/进入淡入淡出过渡中简单地将导航/状态栏背景排除为目标。有关详细信息,请参见我更新的答案。
Alex Lockwood 2014年

3

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

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

3

我也遇到了同样的问题,答案似乎缺少这个难题的关键部分。请记住,在共享元素过渡上,所有事情都在目标活动中发生

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

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

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

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


你好 这与顶部答案中的方法1相同吗?
rlay3

@ rlay3不完全是。据我所知,他从未提及只需要在目标活动中设置这一事实。
LukeWaggoner '18

嘿@LukeWaggoner您能帮我吗:stackoverflow.com/questions/50189286/…–
blackHawk

您必须在呼叫者和被呼叫者这两个活动中都排除状态栏和导航栏。
Giovanni Di Gregorio


0

这是我的方法。我同意这两个Status BarNavigation BarSharedElementTransition与一起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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.