Android - 使用Glide在不同scaleType的ImageView之间进行共享元素转换时,动画效果不正确

5
请注意,这个问题与这里找到的问题非常相似。没有解决办法,所以我想提供一些我的代码并再次询问。
基本上,我正在尝试在两个片段之间进行ImageView的共享元素转换。我的最终目标是拥有一个看起来非常类似于默认图库的动画。重要的一点是,我正在使用Glide加载我的图像。我遇到的问题是我无法使scaleType平滑地动画化(从centerCrop到fitCenter)。 我有一个包含RecyclerView的片段,其项每个包含一个ImageView。这些ImageView旨在成为缩略图,XML如下:
<ImageView
    android:id="@+id/ivPhoto"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_alignParentEnd="true"
    android:layout_centerVertical="true"
    android:layout_marginEnd="@dimen/large_spacing"
    android:scaleType="centerCrop"/>

重要/有问题的部分是scaleType="centerCrop" 我使用以下方式在RecyclerView适配器中设置转换名称并加载图像:
holder.ivPhoto.setTransitionName(current.getPhotoFilepath());
Glide.with(holder.ivPhoto.getContext())
        .load(current.getPhotoFilepath())
        .dontTransform()
        .into(holder.ivPhoto);

当一个项目被点击时,将运行以下代码:
@Override
public void onItemClicked(AssetDetail assetDetail, ImageView imageView) {
    if (assetDetail.getPhotoFilepath() != null) {
        AssetDetailFragmentDirections.ActionAssetDetailFragmentToDisplayImageFragment action = AssetDetailFragmentDirections.actionAssetDetailFragmentToDisplayImageFragment(assetDetail.getPhotoFilepath());
        FragmentNavigator.Extras extras = new FragmentNavigator.Extras.Builder()
                .addSharedElement(imageView, assetDetail.getPhotoFilepath())
                .build();
        NavHostFragment.findNavController(this).navigate(action, extras);
    }
}

这将带您到第二个片段,其基本上只包含以下ImageView(技术上是PhotoView,我也尝试过使用普通的ImageView):
<com.github.chrisbanes.photoview.PhotoView
    android:id="@+id/ivImage"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter" />

我推迟进入转换并设置我想要使用的转换:
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    postponeEnterTransition();
    TransitionSet transitionSet = new TransitionSet()
            .addTransition(new ChangeBounds())
            .addTransition(new ChangeImageTransform())
            .setDuration(1500)
            .setOrdering(TransitionSet.ORDERING_TOGETHER)
            .setInterpolator(new FastOutSlowInInterpolator());
    setSharedElementEnterTransition(transitionSet);
}

最后,我获取并设置了过渡名称,加载图像并启动延迟的进入过渡:
DisplayImageFragmentArgs args = DisplayImageFragmentArgs.fromBundle(getArguments());
binding.ivImage.setTransitionName(args.getImageFilepath());

Glide.with(requireContext())
        .load(args.getImageFilepath())
        .listener(new RequestListener<Drawable>() {
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                startPostponedEnterTransition();
                return false;
            }

            @Override
            public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                startPostponedEnterTransition();
                return false;
            }
        })
        .dontTransform()
        .into(binding.ivImage);

这导致该动画完全错误。
如果我像这样交换转换顺序:
TransitionSet transitionSet = new TransitionSet()
        .addTransition(new ChangeImageTransform())
        .addTransition(new ChangeBounds())
        .setDuration(1500)
        .setOrdering(TransitionSet.ORDERING_TOGETHER)
        .setInterpolator(new FastOutSlowInInterpolator());

我理解这句话的意思是:“我找到了更接近的动画this animation,但进入过渡效果从一个奇怪的位置开始,而返回过渡效果在包含ImageView的CardView之后/内部。”第二段话的意思是:“根据this article尝试一些不同的过渡效果。”
TransitionSet transitionSet = new TransitionSet()
        .addTransition(new ChangeClipBounds())
        .addTransition(new ChangeTransform())
        .addTransition(new ChangeBounds())
        .setDuration(1500)
        .setOrdering(TransitionSet.ORDERING_TOGETHER)
        .setInterpolator(new FastOutSlowInInterpolator());

this中,您可以看到scaleType立即更改,然后其他动画发生。我希望scaleType的更改与其他内容一起平滑地进行动画处理。

有趣的是,在RecyclerView中的ImageView中删除scaleType="centerCrop"后,最终过渡的组合就会完美地进行动画处理,但显然这意味着图像具有其原始宽高比,而不是适当的缩略图。请参见此处

问题显然与scaleType有关,但无论我尝试什么,都似乎无法使其正常工作。我已经做了大量的谷歌搜索和阅读,但没有找到解决问题的方法。

据我所了解,使用ChangeImageTransform应该可以动画化scaleType,但是使用任何我尝试过的组合(包括与ChangeTransform一起尝试)似乎都无法正常工作,或者至少不是我想要/期望的方式。我已经超出了沮丧的程度。

如果有人能给我任何帮助来解决这个问题,我将非常感激。

1个回答

0

最简单的解决方案可能是改用 Picasso 而不是 Glide,就像 Shared Element with scaleType centerCrop transition is jumpy 的提问者最终所做的那样。

问题在于 Glide 重设图像大小以适应每个 ImageView 以实现内存节省的方式。如果缩略图 ImageView 大小与 ViewPager 大小不匹配,则会破坏共享元素转换的 ChangeImageTransform。

首先,您的转换集应包含:

<changeClipBounds/>
<changeTransform/>
<changeBounds/>
<changeImageTransform/>

关键是将其添加到Glide请求中:

.dontTransform()
.override(screenWidth, screenHeight)

然而,这将导致您的网格片段使用高内存,因为每个图像将以比缩略图所需的尺寸大得多的尺寸加载。您将遇到滚动缓慢和崩溃的问题。

因此,您可以默认以旧方式加载图像,然后在转换之前,在单击网格项时加载全屏大小的图像。您还需要推迟Pager片段事务,直到完整的图像加载完成,以便转换可以快照新的ImageView属性。

onClickGridItem() {
    Glide.with(imageView)
        .load(uri)
        .listener(RequestListener<Drawable> {
            onResourceReady() {
                openPagerFragment()
                return false
            }
        })
        .dontTransform()
        .override(screenWidth, screenHeight)
        .into(imageView)
}

在切换图像大小时,可能会出现不必要的闪烁。您可以通过在将其设置为ImageView之前预加载全尺寸图像来避免这种情况:

Glide.with(imageView)
    .load(uri)
    .listener(RequestListener<Drawable> {
        onResourceReady() {
            Glide.with ... .override(screenWidth, screenHeight).into(imageView)
            return false
        }
    })
    .dontTransform()
    .preload(screenWidth, screenHeight)

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