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

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

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

根据 文件:

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

我在调用和调用活动中都使用 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

类似地,如果我将输入转换更改为幻灯片,状态栏和导航栏都会有一个白色背景的幻灯片转换。

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

49608 次浏览

你需要在 ActivityOptions.makeSceneTransitionAnimation.中共享它们

例如:

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

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

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

据我所知,有两种方法可以防止导航/状态栏在转换过程中产生动画效果:

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

导航/状态栏在转换过程中淡入淡出的原因是,默认情况下,一旦转换开始,所有非共享视图(包括导航/状态栏背景)将在调用/调用 Activitys 中分别淡出/淡入。但是,您可以通过从窗口的默认退出/进入 Fade转换中排除导航/状态栏背景来轻松地绕过这个问题。只需将以下代码添加到 Activity 的 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 建议在他的答案。然而,为了防止状态栏和导航栏在转换过程中“闪烁”,我还需要在 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 + 的文章中找到。

我是这么做的。我共享 SharedElementTransition中的 Status BarNavigation Bar以及 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);
}

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

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

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

在输入活动时,调用 getWindow ()

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

我怀疑这可能有副作用,但不能肯定。这是非常简单,但工程。

防止特定元素闪烁:

我从 Alex Lockwood 的回答开始,做了相当多的实验,试图让它工作。它的核心是正确的,虽然我不需要他为接收活动建议的代码,但我遇到了一些问题,通过调用一个片段(而不是一个活动)和设置一个工具栏作为操作栏。

碎片的事吗?我看到很多评论说,试图检索对状态栏和导航栏的引用是空的。同样的事情也发生在我身上,直到我意识到我不会在碎片的布局中找到它们... 它们在那个层次之上。因此,下面的代码可以从 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();
}

注意 过渡 _ 应用程序布局Rid 应用程序布局。这些 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>

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

然后你会在你的片段中这样称呼它:

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

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

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

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

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

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

我也遇到了同样的问题,而答案似乎缺少了一个关键的部分。请记住,在共享元素转换中,所有事情都发生在 目的地活动中。

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

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


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

这应该能解决你的问题!

在动画的情况下

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

如果有人使用安卓系统
fade.excludeTarget(android.R.id.navigationBarBackground, true);
不工作,这只是因为这个窗口不能找到这个 id 的视图,你应该替换为 this.excludeTarget(androidx.appcompat.R.id.action_bar_container,true).

但现在是 导航时间,所以悲伤知道这一点,直到现在。