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());
}
}
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();
}
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.
EDIT:
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.
EDIT:
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.
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;
}
});
}
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()
.
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);
}
}
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.
来源:https://stackoverflow.com/questions/49806441/shared-element-transition-fragment-recyclerview-viewpager