当前位置: 首页 > 知识库问答 >
问题:

CardView上具有半径的共享元素转换

何兴邦
2023-03-14

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

我有一个CardView,它包含一个带有ImageView的线性布局。

没有那个半径共享元素过渡可以无缝工作。但是,当我将半径(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();

这是可行的,但看起来并不太好,因为过渡会在开始过渡之前等待动画完成。我需要的是在转换到新活动时设置radius动画(类似于在TransitionSet中一起排序)。

第二种方法-ChangeImageTransform

我读过一篇StackOverflow文章,介绍如何使用像ChangeImageTransform和ChangeBounds这样的转换类。

我确实像建议的那样定义了我的应用程序主题(我的转换包含ChangeImageTransform transitionSet)

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

但它不起作用。。

第三种方法-幼稚

我最后一次尝试是强制目标ImageView的半径也为25dp。因为我的CardView可能被转换成正方形,因为目标ImageView是正方形的,但正如你所猜测的,它不起作用。

第四种方法-不使用CardView

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

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

共有3个答案

诸新霁
2023-03-14

我无法将其用于片段共享元素转换。在动画期间,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) {}
});

基本上,ImageView本身有自己的圆角,而不是为CardView的圆角设置动画。

ImageView的角使用ViewOutlineProvider圆角,可以使用ObjectAnimator在播放共享元素转换时为角半径设置动画。请注意,还需要在ImageView上调用setClipToOutline(true),否则将不会剪裁角点。

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

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

通过一些努力,这可以变成一个常规的过渡,您只需将其添加到一组共享元素过渡中,即可自动确定如何处理共享元素

宗政昱
2023-03-14

根据Ovidiu的回答,这里有一个工作过渡,可以使角落充满活力

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

这可以以编程方式添加到Enter共享转换集或通过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>
孟茂学
2023-03-14

我终于解决了。对于那些感兴趣的人,以下是方法:

为什么在开始过渡之前删除半径?因为目标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时,它并不明显,但实际上它变成了目标共享视图。

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>

ListActivity.class

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

startActivity(intent, option.toBundle());

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();
            }
        });
}

注:布局和活动的要点

 类似资料:
  • 有没有办法让CardView在顶部只有角半径?

  • 我有一个元素,其中左上角和右下角的边框半径为5,我试图使它与CSS3Pie兼容。文件指出 只支持速记版本;longhand border-top-left-radius等属性不是。不过,速记语法支持每个角的半径不同。链接

  • 我一直在尝试实现这个共享元素转换,并在单击转换时不断出现“java.lang.IllegalArgumentException:共享元素不能为null”错误。请帮忙。 这是下面给出的MainActive onCreate方法。请检查我的代码。 这是recyclerView的onClickListener。 图像的过渡名称相同。这里的bug修复在使用共享元素的活动转换中出现问题并不是问题所在。 我卡

  • 我正在实现一个gallery应用程序,它有一个片段,其中包含一个带有图像的RecyclerView,单击一个图像,我会转到ViewPager循环浏览图像 目前,我正试图实现像本视频中那样的入门动画。问题是动画不起作用,我显然遗漏了一些东西(只是显示与转换相关的代码): 查看页面: GridAdapter: 在MainActivity中,我在onClick中实例化ViewPagerFragment:

  • 我在Lollipop上的共享元素转换中看到了奇怪的事情。共享元素在开始动画之前闪烁(请看视频https://www.youtube.com/watch?v=DCoyyC_S-9A) 我不知道为什么会这样。但是,当我添加

  • 当完成活动时,我正在更新包含viewpager的活动中的视图及其名称,但它会闪烁: