【问题标题】:Shared Element Transition on CardView with radiusCardView 上的共享元素转换与半径
【发布时间】:2017-07-05 14:28:24
【问题描述】:

我已经解决这个问题好几个星期了,但我仍然无法解决这个问题。

所以,我有一个 CardView,它包含一个带有 ImageView 的 LinearLayout。

如果没有该半径,共享元素过渡可以无缝运行。但是,当我将半径 (app:cardCornerRadius="25dp") 添加到该 CardView 时,共享元素转换看起来很丑,因为它首先删除半径,然后开始动画。

第一种方法:ObjectAnimator

我创建 ObjectAnimator 来为卡片上的半径值设置动画,动画结束后开始过渡。

ObjectAnimator animator = ObjectAnimator
            .ofFloat(view, "radius", AppUtil.dpAsPixel(this, 25), 0);
animator.setDuration(150);
animator.addListener( // start new Activity with Transition );
animator.start();

这可行,但看起来不太好,因为过渡会在开始过渡之前等待动画完成。我需要的是在过渡到新 Activity 时半径正在设置动画(类似于 TransitionSet 中的 ORDERING_TOGETHER)。

第二种方法 - ChangeImageTransform

我已经阅读了 StackOverflow post 以使用像 ChangeImageTransform 和 ChangeBounds 这样的转换类。

我确实按照建议定义了我的应用程序主题(my_transition 包含 ChangeImageTransform 转换集)

<item name="android:windowSharedElementEnterTransition">@transition/my_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/my_transition</item>

但它不起作用..

第三种方法 - 天真

我最后一次尝试是强制目标 ImageView 的半径也为 25dp。因为也许我的 CardView 变成了方形,因为目标 ImageView 是方形的,但是你可以猜到,它不起作用。

第四种方法 - 不使用 CardView

如您所见,我正在使用企鹅图像并使用 CardView 来制作半径。我可以使用图像转换使图像变圆,但我仍然认为这不是创建共享元素转换的正确方法..

这是我的问题,有没有办法在不先移除半径的情况下使用 CardView 半径进行共享元素转换?

【问题讨论】:

标签: android android-animation android-cardview android-transitions shared-element-transition


【解决方案1】:

我终于能够解决它。有兴趣的朋友可以看看:

为什么在开始过渡之前删除半径?因为目标 ImageView 没有任何半径。

activity_detail.xml

<ImageView
    android:id="@+id/iv_image_cover"
    android:layout_width="match_parent"
    android:layout_height="250dp"
    android:scaleType="centerCrop"
    android:src="@{animal.imageRes}"
    android:transitionName="animalImage"
    tools:src="@drawable/acat"
/>

当我使用不带radius的CardView时,不明显,但实际上变成了目标共享视图。

  1. 要实现半径到无半径的过渡,您必须将目标共享视图设置为圆角。我只是使用卡片视图(带半径)来包装它。

activity_detail.xml

<android.support.v7.widget.CardView
    android:id="@+id/card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:transitionName="card"
    app:cardCornerRadius="25dp"
>

    <ImageView
        android:id="@+id/iv_image_cover"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:scaleType="centerCrop"
        android:src="@{animal.imageRes}"
        android:transitionName="animalImage"
        tools:src="@drawable/acat"
    />

</android.support.v7.widget.CardView>
  1. 务必将 makeSceneTransition 更改为使用“card”而不是“animalImage”

ListActivity.class

ActivityOptionsCompat option = ActivityOptionsCompat
.makeSceneTransitionAnimation(ListActivity.this, cardView, "card");

startActivity(intent, option.toBundle());
  1. 在 DetailActivity 中,您可以在过渡开始时启动半径动画。

DetailActivity.java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().getSharedElementEnterTransition()
        .addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {
                ObjectAnimator animator = ObjectAnimator
                    .ofFloat(activityDetailBinding.card, "radius", 0);
                animator.setDuration(250);
                animator.start();
            }
        });
}
  1. 享受平稳过渡

注意:layout 和 activities 的要点

【讨论】:

  • 您的解决方案就像魅力一样!!,我对此有 1 个问题,是否因为这种过渡,活动开始更慢(活动需要 2 秒打开)有什么想法吗?
  • 你的意思是因为这种转变,活动需要更长的时间来启动@UttamPanchasara?根据我的经验,开始活动不应该花费大量时间,尤其是 2 秒。要对此进行测试,您可以在设备的设置中禁用动画/过渡。如果它仍然很慢,那么问题不在于动画/过渡。
  • 好的,,但是在我的情况下,当我点击 recyclerview 的项目并移动到详细信息屏幕时,打开详细信息屏幕需要暂停,就像不能顺利打开一样......有什么想法吗?我参考了几乎所有的谷歌转换示例。谢谢
  • 退出过渡怎么样?
  • 关于退出过渡:创建一个名为exiting的变量,默认设置为false。覆盖 onBackPressed 并将其设置为 true,然后在 onTransitionStart 内部调用 super.onBackPressed() 或 supportFinishAfterTransition(),检查退出是否为 true,如果是:ObjectAnimator animator = ObjectAnimator.ofFloat(card, "radius", resources.getDimension (R.dimen.news_item_card_radius)) animator.setDuration(250);动画师.start();
【解决方案2】:

根据 Ovidiu 的回答,这是一个可以为角落设置动画的工作过渡

https://gist.github.com/StefanDeBruijn/d45807d386af0e066a03186fe00366e8

这可以通过编程方式添加到输入共享转换集或通过 xml:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <transitionSet>
        <targets>
            <target android:targetId="@id/backdrop" />
        </targets>
        <!-- Custom transition to take care of rounded corner to square corners transition -->
        <transition
            class=".ChangeOutlineRadius"
            app:endRadius="@dimen/square_corner_radius"
            app:startRadius="@dimen/default_corner_radius" />
        <!-- Default shared element transitions -->
        <changeBounds />
        <changeTransform />
        <changeClipBounds />
        <changeImageTransform />
    </transitionSet>
</transitionSet>

不要忘记添加到 attrs.xml:

   <declare-styleable name="ChangeOutlineRadius">
        <attr name="startRadius" format="dimension" />
        <attr name="endRadius" format="dimension" />
    </declare-styleable>

【讨论】:

  • 这是否需要目标ImageView 也设置一个半径?
  • @NielsMasdorp 不,只需将其作为匹配父子添加到您的 CardView 中,半径为
【解决方案3】:

我无法让它与 Fragment 共享元素转换一起使用。 CardView 的圆角半径将在动画期间被忽略。下面是一些有效的方法:

fragment2.setEnterSharedElementCallback(new SharedElementCallback() {
    @Override
    public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {}

    @Override
    public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
        ImageView sharedImageView = null;

        for (View view : sharedElements) {
            if (view instanceof ImageView) {
                sharedImageView = (ImageView) view;
                break;
            }
        }

        if (sharedImageView != null) {
            sharedImageView.setClipToOutline(true);

            ObjectAnimator.ofInt(sharedImageView, new Property<ImageView, Integer>(Integer.class, "outlineRadius") {
                @Override
                public Integer get(ImageView object) {
                    return 0;
                }

                @Override
                public void set(ImageView object, final Integer value) {
                    object.setOutlineProvider(new ViewOutlineProvider() {
                        @Override
                        public void getOutline(View view, Outline outline) {
                            outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), value);
                        }
                    });
                }
            }, 150, 0).setDuration(duration).start();
        }
    }

    @Override
    public void onRejectSharedElements(List<View> rejectedSharedElements) {}

    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}
});

基本上,不是对 CardView 的圆角进行动画处理,而是 ImageView 本身有自己的圆角动画。

ImageView 的角使用 ViewOutlineProvider 进行圆角处理,并且 ObjectAnimator 可用于在播放共享元素过渡时为角半径设置动画。注意在ImageView上调用setClipToOutline(true)也是必须的,否则不会剪掉边角。

回调的onSharedElementEnd 方法将使用所有共享元素的列表进行调用。请注意,我的示例代码仅处理正在共享的 ImageView 之一的角动画。如果您的过渡共享多个 ImageView,您也需要将这些考虑在内。

还要注意,由于某种原因,在播放反向转换时也会调用相同的回调。

通过一些努力,这可以变成一个常规的转换,您只需将其添加到共享元素转换集,它会自动确定如何处理共享元素。

【讨论】:

  • 这就像一个魅力,据我所知,任何以前的反对票都是没有实际意义的。这是迄今为止最优雅的解决方案。
【解决方案4】:

我在这里尝试了所有解决方案,但对我没有任何效果。我需要从centerInside 比例类型转到fitCenter。这在使用建议在卡片视图上应用动画的解决方案时产生了问题。 (在这种情况下,比例类型没有动画)。

所以为了解决这个问题,在父 ImageView 本身中,我应用了一个圆角:

    val radius = context.getDimensionPixelOffset(R.dimen.radius).toFloat()
    image_view.outlineProvider = object : ViewOutlineProvider() {
        override fun getOutline(view: View, outline: Outline) {
            outline.setRoundRect(
                0, 0, view.width,
                view.height, radius
            )
        }
    }
    image_view.clipToOutline = true

不需要做任何其他事情,Transition Animation API 会处理剩下的事情。

和平。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    相关资源
    最近更新 更多