Android共享元素过渡ToolBar重叠

时间:2017-06-11 12:40:22

标签: android android-fragments android-animation shared-element-transition

我为我的应用程序实现了共享元素转换,其中转换从主屏幕上的ViewPager内的Fragment(使用RecyclerView)的图像开始,并再次扩展到全屏幕库视图,再次位于ViewPager的片段中。这一切都运行正常,但如果图像不完全可见,它会在展开到全屏之前进入TabBar的顶部。这是正在发生的事情:

enter image description here

我的输入转换如下所示:

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

退出:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together"
android:duration="500">
    <fade>
        <targets>
            <target android:excludeId="@android:id/statusBarBackground" />
            <target android:excludeId="@android:id/navigationBarBackground" />
        </targets>
    </fade>
</transitionSet>

在调用活动的共享元素回调中,我得到了这个:

View navigationBar = activity.findViewById(android.R.id.navigationBarBackground);
View statusBar = activity.findViewById(android.R.id.statusBarBackground);
if (navigationBar != null) {
    names.add(navigationBar.getTransitionName());
    sharedElements.put(navigationBar.getTransitionName(), navigationBar);
}
if (statusBar != null) {
    names.add(statusBar.getTransitionName());
    sharedElements.put(statusBar.getTransitionName(), statusBar);
}

最后在styles.xml中的活动主题:

<item name="android:windowContentTransitions">true</item>
<item name="android:windowEnterTransition">@transition/details_window_enter_transition</item>
<item name="android:windowReturnTransition">@transition/details_window_return_transition</item>

我真的不明白如何在没有获得此重叠的情况下通过转换排除工具栏(或操作栏)。也许一种方法是以某种方式强制图像在顶部被剪裁,以便在工具栏下方并且仅从可见矩形展开时它不会完全可见。

我尝试将<target android:excludeId="@id/action_bar_container"/>添加到动画的目标中,但同样的事情仍然存在。

欢迎任何建议。

4 个答案:

答案 0 :(得分:9)

我在我的项目中发现了类似的问题。在你的风格中添加以下代码。

<item name="android:windowSharedElementsUseOverlay">false</item>

它对我有用。

答案 1 :(得分:2)

我想出了一个临时的解决方法。在执行共享元素转换之前,调用活动检查目标视图是否在RecyclerView的范围内。如果没有,则RecyclerView将平滑滚动以显示完整视图,一旦完成,转换就会运行。如果视图完全可见,则转换正常运行。

// Scroll to view and run animation when scrolling completes.
recycler.smoothScrollToPosition(adapterPosition);
recycler.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

    @Override
    public boolean onPreDraw() {
        recycler.getViewTreeObserver().removeOnPreDrawListener(this);
        // Open activity here.
        return true;
    }
});

这是结果,在我找到更好的解决方案之前不会太糟糕:

enter image description here

答案 2 :(得分:1)

我到处搜索,找不到任何解决方案,所以我想通了自己。这是一个录制的演示(速度的一半),用于显示结果(有或没有修复)。

With fix

请在此处签出完整的工作演示: Without fix

步骤

让我们说我们有两个活动,即。 MainActivity带有一个滚动容器,其中包含一个滚动网格/缩略图列表,而我们有一个SecondActivity可以全屏幻灯片形式显示图像。

请签出完整代码以完全了解该解决方案。

  1. 在托管滚动容器的MainActivity内,在缩略图上设置点击侦听器以打开SecondActivity
ImageView imageView = findViewById(R.id.image_view);
imageView.setOnClickListener(v -> {
    // Set the transition name. We could also do it in the xml layout but this is to demo
    // that we can choose any name generated dynamically.
    String transitionName = getString(R.string.transition_name);
    imageView.setTransitionName(transitionName);

    // This part is important. We first need to clip this view to only its visible part.
    // We will also clip the corresponding view in the SecondActivity using shared element
    // callbacks.
    Rect localVisibleRect = new Rect();
    imageView.getLocalVisibleRect(localVisibleRect);
    imageView.setClipBounds(localVisibleRect);
    mClippedView = imageView;

    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    intent.putExtra(SecondActivity.EXTRA_TRANSITION_NAME, transitionName);
    intent.putExtra(SecondActivity.EXTRA_CLIP_RECT, localVisibleRect);
    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(
                    MainActivity.this,
                    Pair.create(imageView, transitionName));
    startActivity(intent, options.toBundle());
});
  1. 将剪辑恢复到onResume()的{​​{1}}中。
MainActivity
  1. 在您的res文件夹中创建过渡资源,如下所示: @Override protected void onResume() { super.onResume(); // This is also important. When we come back to this activity, we need to reset the clip. if (mClippedView != null) { mClippedView.setClipBounds(null); } } 内容应与此类似:
app/src/main/res/transition/shared_element_transition.xml
  1. 在您的<?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:duration="375" android:interpolator="@android:interpolator/fast_out_slow_in" android:transitionOrdering="together"> <!-- This is needed to clip the invisible part of the view being transitioned. Otherwise we will see weird transitions when the image is partially hidden behind appbar or any other view. --> <changeClipBounds/> <changeTransform/> <changeBounds/> </transitionSet> 中设置过渡。
SecondActivity
  1. 现在,我们还需要在@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); // Setup transition Transition transition = TransitionInflater.from(this) .inflateTransition(R.transition.shared_element_transition); getWindow().setSharedElementEnterTransition(transition); // Postpone the transition. We will start it when the slideshow is ready. ActivityCompat.postponeEnterTransition(this); // more code ... // See next step below. } 中裁剪共享视图。
SecondActivity

答案 3 :(得分:0)

我想我对这个问题有正确的答案。发生此问题的原因是,在第二个活动中根本没有工具栏,因此无法将其添加为共享元素来进行过渡。因此,在我的情况下,我在第二个活动中添加了一些“假工具栏”,高度为0dp。然后,您可以将第一个活动中的工具栏添加为共享元素,并给他更改边界动画,这样工具栏将与图像同时折叠并且图像将不再位于工具栏上方。

我的“假工具栏”视图:

    <View
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:background="@color/colorPrimary"
    android:elevation="10dp"
    android:outlineProvider="none"
    android:transitionName="toolbar_transition" />

重要说明:

-视图必须具有非透明背景

-我添加了海拔,以确保我的视图在图片上方

-高程会引起阴影,我不希望这样,所以我将outilenProvider设置为none

接下来要做的就是将工具栏添加到共享元素中

    sharedElements.add(new Pair<>(toolbar, "toolbar_transition"));