共享元素转换 + 片段 + 可滚动视图 + 视图页面

5

我正在实现一个画廊应用,其中有一个Fragment持有一个包含图片的RecyclerView,在点击一张图片后跳转到ViewPager来循环浏览图片。
目前,我正试图仅实现类似这个视频中所示的入场动画。问题在于动画根本不起作用,我显然漏掉了些东西(下面是与转换相关的关键代码):

ViewPager:

public class ViewPagerFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_viewpager, container, false);
        Transition transition = TransitionInflater.from(getContext()).inflateTransition(R.transition.fragment_transition);

        setSharedElementEnterTransition(transition);
        postponeEnterTransition();
        return view;
}

GridAdapter:

public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.setPhotoImage(new File(mArrayOfPhotos.get(position).photo));
        ViewCompat.setTransitionName(holder.photoImage, mPhotoObjects.get(position).photo);
    }

    @Override
    public void onClick(View view) {
        // pass an image view that is being clicked... 
        // ...via listener to MainActivity from where ViewPagerFragment is started
        mListener.onImageSelected(photoImage, getAdapterPosition());
    }
}

在MainActivity中,我在onClick中实例化ViewPagerFragment:
@Override
public void onImageSelected(View view, int clickedImagePosition) {
    ViewPagerFragment fragment = new ViewPagerFragment();
    Bundle bundle = new Bundle();
    bundle.putInt(ViewPagerFragment.KEY_IMAGE_INDEX, clickedImagePosition);
    fragment.setArguments(bundle);
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    // view is an image view that was clicked
    fragmentTransaction.addSharedElement(view, ViewCompat.getTransitionName(view));
    fragmentTransaction.setReorderingAllowed(true);
    fragmentTransaction.replace(R.id.fragment_placeholder, fragment, VIEWPAGER_FRAGMENT);
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

最后,在ImageFragment(ViewPager中的单个图像)中我有:

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mFullImage.setTransitionName(transitionName);

    Glide.with(getActivity())
            .load(myImage)
            .apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
            .listener(new RequestListener<Drawable>() {
                @Override
                public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                    getParentFragment().startPostponedEnterTransition();
                    return false;
                }

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

    return view;
}

转换名称不是问题,它是唯一的,并且在所有片段之间正常传递。我遵循了这篇文章并阅读了许多其他文章,但感觉我的代码中缺少了一小部分。
编辑:我认为我已经找到了问题所在。过渡效果会随机地工作几次,显然,这是因为当我进行转换时图像尚未准备好。我尝试在MainActivity onCreate中调用postponeEnterTransition(),但仍然无法正常工作。图像通过Bundle传递到ImageFragment。仍在寻找解决方案。
编辑:
我相信我已经找到了问题所在,我有一个片段,在其中包含一个TabLayout,它有2个片段,一个显示所有照片,一个显示标记为收藏的照片。两者都使用相同的RecyclerView。只有当照片同时位于一个部分时,共享元素转换才起作用。一旦我将照片标记为喜欢,它就会出现在所有照片以及喜欢的照片中,转换就不再起作用。这可能是因为转换不再是唯一的。是否有解决此问题的方法?考虑到两个片段都使用相同的RecyclerView和相同的适配器类。

你也看了这篇文章吗?http://mikescamell.com/shared-element-transitions-part-4-recyclerview/ - Deepak kaku
@Deepakkaku非常感谢您提供的链接,不幸的是,我也查看了它,但我还是按照我的方式进行了操作,因为它更加详细和最新。不过我部分地参考了它。我认为我的代码中有一个小错误。 - Suleyman
你所说的“不起作用”具体指什么?是应用程序崩溃了吗?还是只是动画效果不正常?如果您能提供任何屏幕录像或视频,展示一下动画出现的问题,那就更好了。谢谢! - Nilesh Deokar
@NileshDeokar 谢谢你的时间!没有任何动画,无论我是否添加共享元素都没有区别。我尝试在XML文件中更改持续时间,但没有变化。我认为某些原因导致动画甚至没有应用。 - Suleyman
1个回答

3

我终于让它工作了,问题在过渡名称中。转换还使用支持Transition库。
起初,转换根本没有起作用,所以我使用了一个方法,我在这里找到的,来推迟转换,这是一个解释转换的很好的链接:

private void scheduleStartPostponedTransition(final View sharedElement) {
sharedElement.getViewTreeObserver().addOnPreDrawListener(
    new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            sharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
            getParentFragment().startPostponedEnterTransition();
            return true;
        }
    });
}

在我的情况下,我有嵌套的片段,一个包含ViewPager并使用ImageAdapter管理片段(ImageFragment)的ViewPagerFragment。我从ViewPagerFragment中调用postponeEnterTransition(),这就是我为什么要使用getParentFragment()的原因。
我在Glide的onLoadFailed和onResourceReady()中使用scheduleStartPostoponedTransition方法。主要问题在于我的实现方式。我有一个包含TabLayout的片段,其中有2个选项卡,每个选项卡都包含一个带有RecyclerView的片段。一个片段显示所有照片,另一个片段显示收藏的照片。如果一张照片只在一个标签页中,过渡效果会很好(在进行了一些修改后),但是一旦我将其标记为收藏,它就无法在任何标签页中正常工作。这意味着两个标签页具有相同的过渡名称。我的解决方案是根据显示的片段(1表示所有照片,2表示喜欢的照片),向GridAdapter传递一个整数,并将其设置为过渡名称:
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.setPhotoImage(new File(mArrayOfPhotos.get(position).photo));
        ViewCompat.setTransitionName(holder.photoImage, mPhotoObjects.get(position).photo + fragmentNumber);
    }
}

然后将这个数字传递给ImageFragment并在那里设置它。之后,它开始工作了,因为每张照片都有一个不同的过渡名称。
另外,如果您正在使用支持版本的转换,请确保您拥有支持库版本27.0.0或更高版本,因为他们在这个版本中修复了转换问题。

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