共享元素使用centerCrop缩放类型的转换效果不流畅。

14

我正在尝试实现一个共享元素的过渡动画,当从一个屏幕跳转到下一个屏幕时,其中有两个ImageViews。在两个屏幕上,其中一个图像都具有centerCrop的比例。问题是,当过渡开始时,图像会返回到它裁剪之前的原始大小,然后才能动画到下一个屏幕。当它到达下一个屏幕时,它将以原始大小到达,并且只有在到达后才被裁剪到目标图像视图。

整个体验不够流畅,对眼睛来说真的很跳跃。我使用的代码是:

public void animateIntent(View view, ArticleCoverData articleCoverData) {
    Intent intent = new Intent(mActivity, ReaderActivity.class);
    intent.putExtra(Constants.ARTICLE_DATA, articleCoverData);

    // The view animation will start from
    View coverStartView = view.findViewById(R.id.coverImage);
    View coverDiagonalDecoratorStartView = view.findViewById(R.id.diagonal_image_decorator);

    Pair<View, String> pair1 = Pair.create(coverStartView, mActivity.getString(R.string.transition_timeline_cover_image_to_reader_name));
    Pair<View, String> pair2 = Pair.create(coverDiagonalDecoratorStartView, mActivity.getString(R.string.transition_timeline_cover_image_diagonal_decorator_to_reader_name));

    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(mActivity, pair1, pair2);
    ActivityCompat.startActivity(mActivity, intent, options.toBundle());
}

使用以下代码,从网络加载图像,其中使用了Glide

Glide.with(mActivity).load(articleCoverData.mImageUrl).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).dontTransform().into(holder.coverImage);

接收方将按以下方式加载图像:

Glide.with(mContext).load(mData.mImageUrl).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(mCoverImageView);

按照我定义的风格:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Add this line -->
    <item name="android:windowContentTransitions">true</item>

    <!-- This is the color of the Status bar -->
    <item name="colorPrimaryDark">@color/transparent</item>

    <!-- specify shared element transitions -->
    <item name="android:windowSharedElementEnterTransition">
        @transition/change_image_transform</item>
    <item name="android:windowSharedElementExitTransition">
        @transition/change_image_transform</item>
</style>

change_image_transform是:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeImageTransform/>
</transitionSet>

编辑:尝试将过渡效果更改为:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <changeTransform/>
    <changeImageTransform/>
</transitionSet>
或者
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <changeImageTransform/>
</transitionSet>

但结果是一样的。

问题是:我是否漏掉了什么,还需要做些什么才能让这个体验更加顺畅?或者这是一个Android的bug?

4个回答

4

我面临着同样的问题,我已经在我的 Glide 加载请求中使用了 dontAnimate() + dontTransform()

对于我来说,解决方案是移除

clipToPadding="false"
clipChildren="false"

从我的ImageView的容器ViewGroup中获取。

在共享元素过渡期间,当使用scaleType="centerCrop"时,这些属性将使我的图像未被剪裁。


4

终于解决了这个问题。通过以下代码和配置实现了期望的结果:

private static void startAnimatedTransitionIntent(Activity context, View view, ArticleCoverData articleCoverData) {
    Intent intent = new Intent(context, ReaderActivity.class);
    intent.putExtra(Constants.ARTICLE_DATA, articleCoverData);

    View coverStartView = view.findViewById(R.id.coverImage);
    View coverDiagonalDecoratorStartView = view.findViewById(R.id.diagonal_image_decorator);

    Window window = context.getWindow();
    View decor = window.getDecorView();
    View navigationBarStartView = decor.findViewById(android.R.id.navigationBarBackground);

    List<Pair<View, String>> pairs = new ArrayList<>();
    pairs.add(Pair.create(coverStartView, context.getString(R.string.transition_timeline_cover_image_to_reader_name)));
    pairs.add(Pair.create(coverDiagonalDecoratorStartView, context.getString(R.string.transition_timeline_cover_image_diagonal_decorator_to_reader_name)));

    if (navigationBarStartView != null) {
        pairs.add(Pair.create(navigationBarStartView, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
    }

    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(context, pairs.toArray(new Pair[pairs.size()]));
    ActivityCompat.startActivity(context, intent, options.toBundle());
}

我正在使用Picasso和以下代码将图像加载到图像视图中:

Picasso.with(mActivity).load(articleCoverData.mImageUrl).into(articleVH.coverImage);

在接收方,我以相同的方式应用图像:

Picasso.with(mContext).load(mData.mImageUrl).into(mCoverImageView);

样式:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:statusBarColor">@color/app_red</item>
    <item name="android:windowContentTransitions">true</item>

    <!-- This is the color of the Status bar -->
    <item name="colorPrimaryDark">@color/transparent</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>

    <!--<item name="android:windowEnterTransition">@transition/cheeses_enter</item>-->
    <!--<item name="android:windowExitTransition">@transition/cheeses_exit</item>-->

    <item name="android:windowSharedElementEnterTransition">@transition/cheeses_enter</item>
    <item name="android:windowSharedElementExitTransition">@transition/cheeses_exit</item>
</style>

两个过渡效果: cheeses_entercheeses_exit 都是:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeBounds />
    <changeTransform />
    <changeClipBounds />
    <changeImageTransform />
</transitionSet>

重要的是使用ImageView的scaleType参数来应用你的比例类型,而不是使用Picasso crop或Glide crop。
因为如果你这样做,剪切和未剪切的操作将作为转换的一部分执行。

1
这是关键:“重要的是使用ImageView scaleType参数应用您的比例类型,而不是使用Picasso crop或Glide crop。”谢谢。 - Roberto
3
我的情况是,启动活动时具有CenterCrop属性,在打开时很平滑,但在返回动画时会引起问题。第一次动画将缩小到单元格的高度,然后填充宽度。 - Usman Rana
1
我应该做什么修改?开头没问题,但返回时缩放不匹配。 - Usman Rana
@UsmanRana,我也遇到了centerCrop相同的问题。您是否已经找到解决方案?请分享。 - mukesh
@mukesh 默认的转换会让你在一个地方遇到抖动,无论是在打开还是关闭时(如果你在onTransitionStart方法中更改了scaleType)。解决这个问题的想法是不使用centerCrop,而是通过设置scaleType在视图上应用缩放。 - Usman Rana

3
为了回答这个问题(并稍微解释一下),首先只需要使用<changeImageTransform/><changeBounds/>即可完成转换。
问题主要是关于Glide如何在时间轴上处理图像视图/可绘制对象的测量值。由于存在一些延迟,因此不会产生过渡动画。 Picasso可以快速提供基于scaletype的图像位置(矩阵)的值,并使过渡动画正常工作。

0
在你的change_image_transform.xml文件中,修改为以下内容。
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeImageTransform/>
    <changeBounds/>
</transitionSet>

我看到你将图像URL传递给新活动,然后在新活动中,你再次使用Glide加载它?尝试传递图像的位图,我认为会更流畅。 - Fuyuba
是的,但是图片被Glide缓存,所以第二次使用时,会从磁盘加载而不是再次从网络加载。 - Emil Adz

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