共享元素在过渡动画中溢出导航栏

8

当活动从一个切换到另一个时,共享元素会在新的活动中覆盖导航栏,如图片所示。 shared elements overlay navigation bar

您还可以在视频中看到此错误:查看覆盖导航栏


1
此视频为私人视频。:) :P - Pratik Butani
我认为在发布问题之前,你已经知道了答案。 - Pratik Butani
谢谢您的通知。我已更改视频的访问权限。 - Sergei Vasilenko
@PratikButani,我的回答不够好。我认为有更好的答案。 - Sergei Vasilenko
你可以发布你的 XML 吗? - Pratik Butani
3个回答

4

(1) 在您的XML中设置
android:windowSharedElementsUseOverlay="false"以禁用覆盖层。禁用后,共享元素将作为被调用Activity的视图层次结构的一部分绘制,这使得共享元素不可能意外地重叠在系统UI栏的顶部。
不幸的是,禁用此行为可能会引入新问题...
例如,您可能会发现在任一Activity中的非共享视图在过渡到其位置时会剪切共享元素。在大多数情况下,您可以通过在XML中为每个共享元素的父级设置android:cliptoChildren="false""android:clipToPadding="false"来防止这种情况发生,但根据具体用例可能需要进行其他配置。
(2) 将操作栏、状态栏背景和导航栏背景添加为额外的共享元素
通过将系统栏设为共享元素,您可以确保原始共享元素和系统UI在窗口的View层次结构顶部以相同的级别绘制。要获取对这些视图的引用,可以使用以下代码:

View decor = getWindow().getDecorView();
View statusBar = decor.findViewById(android.R.id.statusBarBackground);
View navBar = decor.findViewById(android.R.id.navigationBarBackground);
View actionBar = decor.findViewById(getResources().getIdentifier("action_bar_container", "id", "android"));

您可以参考此帖子了解更多讨论。


1
我发现这是Android的一个bug,当你使用

时。
<item name="android:windowDrawsSystemBarBackgrounds">true</item>

在活动的主题中。

但它需要设置状态栏的颜色,所以我不能将其设置为false。否则,可以通过运行时设置此标志。

if (Build.VERSION.SDK_INT >= 21) {
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}

此外,您还可以通过WindowclearFlags方法清除此标志。然后,在第二个活动中开始转换动画之前清除此标志,并在转换完成后添加此标志。这只需要在被调用的活动中执行。 更新 我找到了更好的解决方案。在Android中,每个共享元素视图都在装饰视图的顶部绘制。导航栏背景通常位于装饰视图的顶部。但在转换时间内,共享元素会更高地排列。
可能会将navigationBarBackground视图添加到共享元素中,从而可以从getWindow().getDecorView().findViewById(android.R.id.navigationBarBackground)访问它。为此,您需要将其添加到调用活动中的共享元素中,并将其添加到被调用活动中的共享元素中。
有一个问题。您需要等待导航栏背景视图附加到装饰视图时。以下是如何在被调用的活动中的onCreate()方法中解决此问题的示例。
ActivityCompat.postponeEnterTransition(this);
final View decorView = getWindow().getDecorView();
decorView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        decorView.removeOnLayoutChangeListener(this);
        View navigationBarBackground = getWindow().getDecorView().findViewById(android.R.id.navigationBarBackground);
        if (navigationBarBackground != null) {
            android.support.v4.view.ViewCompat.setTransitionName(navigationBarBackground, "navigationBg");
        }
        ActivityCompat.startPostponedEnterTransition(MyActivity.this);
    }
});

2
在使用自定义背景时,您应该使用 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAMEWindow.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME 作为转换名称。 - kuelye
终于!你更新后的答案对我起作用了!然而,如果你使用Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME作为transitionName,甚至不需要使用setTransitionName。实际上,在onLayoutChange中调用ActivityCompat.startPostponedEnterTransition(MyActivity.this);就足够了。 - alexbchr
@alexbchr 你测试过所有的Android版本吗,包括 API v21 及以上的版本? - Sergei Vasilenko
到目前为止,我只在API 25和26上测试过它。 - alexbchr

0
如果您正在使用SharedElement转换,您可以通过调用以下方法禁用默认的活动转换(淡入淡出):
getWindow().setEnterTransition(null);

这也将解决共享元素位于状态栏后面且在启动动画时出现故障的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接