【问题标题】:Android shared element transition ToolBar overlapAndroid 共享元素过渡 ToolBar 重叠
【发布时间】:2017-06-11 12:40:21
【问题描述】:

我已经为我的应用程序实现了共享元素转换,其中转换从主屏幕上 ViewPager 内的 Fragment(使用 RecyclerView)的图像开始,然后扩展到全屏画廊视图,再次在 ViewPager 中的 Fragment 内.这一切都很好,除了如果图像不完全可见,它会在扩展到全屏之前位于 TabBar 的顶部。以下是正在发生的事情:

我的输入转换如下所示:

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

我真的不明白如何通过过渡排除工具栏(或操作栏)而不会出现这种重叠。也许一种方法是强制图像在顶部被剪裁,这样它在工具栏下时不会变得完全可见,并且只能从可见矩形展开。

我尝试将&lt;target android:excludeId="@id/action_bar_container"/&gt; 添加到动画的目标中,但同样的事情仍然发生。

欢迎提出任何建议。

【问题讨论】:

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


    【解决方案1】:

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

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

    它对我有用。

    【讨论】:

    • 这将减少共享元素视图的 alpha 值,这很有意义,但在我的情况下看起来不太好。但是,这是一个有效的解决方案,所以我会标记为正确的。
    • 如果有人不愿意禁用windowSharedElementsUseOverlay,那么您可以使用我的解决方案。 stackoverflow.com/a/64183340/978325
    【解决方案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;
        }
    });
    

    这是结果,在我找到更好的解决方案之前还不错:

    【讨论】:

    • 我快疯了,我也有同样的问题!您是如何实现这种滚动的?
    • @user11230 编辑了我的答案,并举例说明了如何滚动并等待滚动完成。
    • 非常感谢,但它不起作用。如果你能请看一看,我将永远感激不尽。自五月以来,我一直在尝试解决此问题。 pastebin.com/uyEpfjvMi.imgur.com/cj3kexs.mp4
    【解决方案3】:

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

    我的“假工具栏”视图:

        <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" />
    

    重要提示:

    -view 必须有非透明背景

    -我添加了高程以确保我的视图“覆盖”了图像

    -elevation 会导致阴影,这是我不想要的,所以我将 outilenProvider 设置为 none

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

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

    【讨论】:

      【解决方案4】:

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

        

      请在此处查看完整的工作演示: https://github.com/me-abhinav/shared-element-overlap-demo

      步骤

      假设我们有两个活动,即。 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()MainActivity 中的剪辑。
      @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);
          }
      }
      
      1. 在 res 文件夹中创建转换资源,如下所示: app/src/main/res/transition/shared_element_transition.xml 内容应该是这样的:
      <?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>
      
      1. 在您的SecondActivity 中设置转换。
      @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.
      }
      
      1. 现在我们还需要剪辑SecondActivity 中的共享视图。
      // Setup the clips
      String transitionName = getIntent().getStringExtra(EXTRA_TRANSITION_NAME);
      Rect clipRect = getIntent().getParcelableExtra(EXTRA_CLIP_RECT);
      setEnterSharedElementCallback(new SharedElementCallback() {
          @Override
          public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
              for (int i = 0; i < sharedElementNames.size(); i++) {
                  if (Objects.equals(transitionName, sharedElementNames.get(i))) {
                      View view = sharedElements.get(i);
                      view.setClipBounds(clipRect);
                  }
              }
              super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots);
          }
      
          @Override
          public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
              for (int i = 0; i < sharedElementNames.size(); i++) {
                  if (Objects.equals(transitionName, sharedElementNames.get(i))) {
                      View view = sharedElements.get(i);
                      view.setClipBounds(null);
                  }
              }
              super.onSharedElementEnd(sharedElementNames, sharedElements, sharedElementSnapshots);
          }
      });
      

      【讨论】:

        猜你喜欢
        • 2019-08-07
        • 2016-02-04
        • 1970-01-01
        • 1970-01-01
        • 2016-04-23
        • 2018-07-14
        • 1970-01-01
        • 2020-04-29
        • 1970-01-01
        相关资源
        最近更新 更多