共享元素过渡 + 片段 + RecyclerView + ViewPager [英] Shared Element Transition + Fragment + RecyclerView + ViewPager

查看:33
本文介绍了共享元素过渡 + 片段 + RecyclerView + ViewPager的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实现一个画廊应用程序,它有一个片段,其中包含一个带有图像的 RecyclerView,单击图像我去 ViewPager 循环图像.
目前,我正在尝试仅实现 this 视频中的入口动画.问题是动画不起作用,我显然遗漏了一些东西(只显示与转换相关的代码):

I am implementing a gallery app, which has a Fragment that holds a RecyclerView with images, onClick of an image I go to ViewPager to cycle through images.
For now, I am trying to implement just the entry animation like in this video. The problem is the animation just doesn't work, I am obviously missing something (just showing code which is relevant to transitions):

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:

In MainActivity I instantiate ViewPagerFragment in onClick:

@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 中的单个图像)中,我有:

And finally in ImageFragment(which is a single image in ViewPager) I have:

@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;
}

过渡名称不是问题,它是唯一的,并且在所有片段之间正常传递.我已关注 这篇 文章并阅读了许多其他内容,但感觉我的代码中缺少一点.

Transition name is not the problem, it is unique, and it is passed normally between all the fragments. I have followed this article and read many others, but it feels like a small bit is missing in my code.


我能够定位我认为的问题.过渡随机工作了好几次,显然,这是因为在我进行过渡时图像还没有准备好.我试图在 MainActivity onCreate 中调用 postponeEnterTransition() 但仍然不起作用.图像通过 Bundle 传递给 ImageFragment.还在寻找.


I was able to localize the problem I think. The transition worked several times randomly, apparently, it happens because images are not ready when I am making the transition. I tried to call postponeEnterTransition() in MainActivity onCreate but still doesn't work. Images are passed to the ImageFragment through the Bundle. Still looking.

我相信我找到了问题,我有一个片段,里面有一个 TabLayout,它有 2 个片段,一个显示所有照片,一个显示标记为收藏的照片.他们都使用相同的RecyclerView.共享元素过渡仅在照片同时位于一个部分中时才有效,一旦我将照片标记为收藏,它就会出现在所有照片以及收藏的照片中,并且过渡不再有效.这可能是因为过渡不再是唯一的.有没有办法解决这个问题?考虑到两个片段使用相同的 RecyclerView 和相同的适配器类.

I believe I found the problem, I have a fragment, that has a TabLayout inside it, which has 2 fragments, one displaying all photos, and one displaying photos that are tagged as favorite. And they both use the same RecyclerView. The shared element transition works only if the photo is in one section at the same time, once I tag a photo as favorite it appears in all photos as well as favorite photos and the transition no longer works. It probably happens because the transition is no longer unique. Is there a way to solve this? Considering the fact that both fragments use the same RecyclerView, and the same adapter class.

推荐答案

我终于搞定了,问题出在过渡名称上.过渡也与支持 Transition 库一起使用.
起初,过渡根本不起作用,所以我使用了我找到的方法 here 推迟过渡,这是一个很好的解释过渡的链接:

I finally got it to work, the problem was in transition names. The transitions are also working with the support Transition library.
At first, the transition was not working at all, so I used a method that I found here to postpone the transition, it's a good link that explains transitions:

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() 的原因.

In my case, I have nested fragments, a ViewPagerFragment that contains a ViewPager and manages the fragments (ImageFragment) using ImageAdapter. I call postponeEnterTransition() from the ViewPagerFragment and that is why I am using getParentFragment().

我在 Glide onLoadFailedonResourceReady() 中使用了方法 scheduleStartPostoponedTransition.
主要问题在于我的实施.我有一个包含 TabLayout 和 2 个选项卡的片段,每个选项卡都包含一个带有 RecyclerView 的片段.一个片段显示所有照片,其他片段显示最喜欢的照片.如果照片仅在一个选项卡中,则过渡效果很好(经过我上面提到的一些更改),但是一旦我将其标记为收藏,它就无法在任何一个选项卡中使用.这意味着两个选项卡的照片具有相同的过渡名称.
我的解决方案是将整数传递给 GridAdapter,具体取决于显示的片段(1 表示所有照片,2 表示最喜欢的照片)并将其设置为过渡名称:

I used the method scheduleStartPostoponedTransition in Glide onLoadFailed and onResourceReady().
The main problem was in my implementation. I have a fragment that contains a TabLayout with 2 tabs, each containing a fragment with a RecyclerView. One fragment shows all photos and other shows favorite photos. The transitions were working fine (after some alteration that I mentioned above) if a photo was only in one tab, but as soon as I marked it as favorite, it wouldn't work in either tab. Which means that two tabs had a photo with the same transition name.
My solution was to pass an integer to the GridAdapter depending on which fragment was shown (1 for all photos and 2 for favorite photos) and set it as a transition name:

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 或更高版本的支持库,因为他们在此版本中修复了过渡.

And then pass this number to the ImageFragment and set it there too. After that, it started to work because every photo now had a different transition name.
Also if you are using a support version of transitions make sure you have the support library version 27.0.0 or above, as they fixed the transitions in this version.

这篇关于共享元素过渡 + 片段 + RecyclerView + ViewPager的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆