共享元素过渡 + 片段 + RecyclerView + ViewPager
Shared Element Transition + Fragment + RecyclerView + ViewPager
我正在实现一个图库应用程序,它有一个 Fragment,其中包含带有图像的 RecyclerView,单击图像时我会转到 ViewPager 以循环浏览图像。
现在,我正在尝试像 this 视频中那样实现入口动画。问题是动画不起作用,我显然遗漏了一些东西(只是显示与转换相关的代码):
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;
}
网格适配器:
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;
}
过渡名称不是问题,它是唯一的,并且在所有片段之间正常传递。我关注了 this 文章并阅读了许多其他文章,但感觉我的代码中缺少一点点。
编辑:
我能够定位我认为的问题。过渡随机工作了几次,显然,这是因为当我进行过渡时图像还没有准备好。我试图在 MainActivity onCreate
中调用 postponeEnterTransition()
但仍然不起作用。图像通过 Bundle 传递给 ImageFragment
。还在寻找。
编辑:
我相信我找到了问题,我有一个片段,里面有一个 TabLayout
,它有 2 个片段,一个显示所有照片,一个显示被标记为收藏的照片。而且他们都使用相同的RecyclerView
。共享元素过渡仅在照片同时位于一个部分时才有效,一旦我将照片标记为收藏,它就会出现在所有照片以及最喜欢的照片中,并且过渡不再有效。这可能是因为过渡不再是唯一的。有办法解决这个问题吗?考虑到两个片段使用相同的 RecyclerView
和相同的适配器 class.
我终于让它工作了,问题出在转换名称上。转换也与支持 Transition
库一起工作。
起初,转换根本不起作用,所以我使用了一个我发现的方法 here 来推迟转换,这是一个很好的 link 解释转换:
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
。
主要问题出在我的实施中。我有一个包含带有 2 个选项卡的 TabLayout
的片段,每个选项卡包含一个带有 RecyclerView 的片段。一个片段显示所有照片,另一个片段显示最喜欢的照片。如果一张照片只在一个选项卡中,过渡效果很好(经过我上面提到的一些更改),但一旦我将其标记为收藏,它就不会在任何一个选项卡中工作。这意味着两个选项卡有一张具有相同转换名称的照片。
我的解决方案是根据显示的片段向 GridAdapter
传递一个整数(1 代表所有照片,2 代表最喜欢的照片)并将其设置为过渡名称:
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
或更高版本,因为他们修复了此版本中的过渡。
我正在实现一个图库应用程序,它有一个 Fragment,其中包含带有图像的 RecyclerView,单击图像时我会转到 ViewPager 以循环浏览图像。
现在,我正在尝试像 this 视频中那样实现入口动画。问题是动画不起作用,我显然遗漏了一些东西(只是显示与转换相关的代码):
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;
}
网格适配器:
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;
}
过渡名称不是问题,它是唯一的,并且在所有片段之间正常传递。我关注了 this 文章并阅读了许多其他文章,但感觉我的代码中缺少一点点。
编辑:
我能够定位我认为的问题。过渡随机工作了几次,显然,这是因为当我进行过渡时图像还没有准备好。我试图在 MainActivity onCreate
中调用 postponeEnterTransition()
但仍然不起作用。图像通过 Bundle 传递给 ImageFragment
。还在寻找。
编辑:
我相信我找到了问题,我有一个片段,里面有一个 TabLayout
,它有 2 个片段,一个显示所有照片,一个显示被标记为收藏的照片。而且他们都使用相同的RecyclerView
。共享元素过渡仅在照片同时位于一个部分时才有效,一旦我将照片标记为收藏,它就会出现在所有照片以及最喜欢的照片中,并且过渡不再有效。这可能是因为过渡不再是唯一的。有办法解决这个问题吗?考虑到两个片段使用相同的 RecyclerView
和相同的适配器 class.
我终于让它工作了,问题出在转换名称上。转换也与支持 Transition
库一起工作。
起初,转换根本不起作用,所以我使用了一个我发现的方法 here 来推迟转换,这是一个很好的 link 解释转换:
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
。
主要问题出在我的实施中。我有一个包含带有 2 个选项卡的 TabLayout
的片段,每个选项卡包含一个带有 RecyclerView 的片段。一个片段显示所有照片,另一个片段显示最喜欢的照片。如果一张照片只在一个选项卡中,过渡效果很好(经过我上面提到的一些更改),但一旦我将其标记为收藏,它就不会在任何一个选项卡中工作。这意味着两个选项卡有一张具有相同转换名称的照片。
我的解决方案是根据显示的片段向 GridAdapter
传递一个整数(1 代表所有照片,2 代表最喜欢的照片)并将其设置为过渡名称:
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
或更高版本,因为他们修复了此版本中的过渡。