これは、android developer blogのContinuous Shared Element Transitions: RecyclerView to ViewPager 記事を解析した内容になります。
今回のサンプルは、RecyclerViewのGrid形式にして、アイテムをタップするとViewPager形式の
ViewにFadeOutしながら遷移するというもので、
ページビューで遷移元とは異なる画像に切り替えて、Grid画面に戻っても
SharedElementをかけています。

実装方法
まず、グリッド画面となるGridFragmentのonCreateViewで
sharedElementなどのアニメーションの設定をします。
| 1 2 3 4 5 6 7 8 9 10 11 |   @Override   public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,       @Nullable Bundle savedInstanceState) {     recyclerView = (RecyclerView) inflater.inflate(R.layout.fragment_grid, container, false);     recyclerView.setAdapter(new GridAdapter(this));     prepareTransitions();     postponeEnterTransition();     return recyclerView;   } | 
prepareTranstion関数では、fragmentを遷移する時の遷移animationとcallbackの設定をしています。
| 1 2 3 4 5 6 7 8 9 10 |   /**    * Prepares the shared element transition to the pager fragment, as well as the other transitions    * that affect the flow.    */   private void prepareTransitions() {     // Fragmentから遷移する際の遷移アニメーションを設定する     setExitTransition(TransitionInflater.from(getContext())         .inflateTransition(R.transition.grid_exit_transition));     // ...   } | 
遷移アニメーション方法はxmlで定義します。
| 1 2 3 4 5 6 7 8 | <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"     android:duration="375"     android:interpolator="@android:interpolator/fast_out_slow_in"     android:startDelay="25">   <fade>     <targets android:targetId="@id/card_view"/>   </fade> </transitionSet> | 
続いて、GridFragmentからViewPagerに遷移する時とViewPagerからGridFragmentに遷移する際
に呼ばれるSharedElementCallbackクラスのセットと実装をします。
onMapSharedElements関数では、
RecyclerViewと対象のPageViewで表示されるImageの遷移先の設定をmapに埋め込んでいます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // A similar mapping is set at the ImagePagerFragment with a setEnterSharedElementCallback. setExitSharedElementCallback(     new SharedElementCallback() {       @Override       public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {         // Locate the ViewHolder for the clicked position.         RecyclerView.ViewHolder selectedViewHolder = recyclerView             .findViewHolderForAdapterPosition(MainActivity.currentPosition);         if (selectedViewHolder == null || selectedViewHolder.itemView == null) {           return;         }         // Map the first shared element name to the child ImageView.         sharedElements             .put(names.get(0), selectedViewHolder.itemView.findViewById(R.id.card_image));       }     }); | 
また、ViewPagerが表示されるImagePageFragmentでも同じように遷移先を
SharedElementsにセットしています。
こう設定することで、ViewPagerで他のimageに切り替えた場合でも
ViewPagerで選択したimageをアニメーション対象にすることができます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | private void prepareSharedElementTransition() {     Transition transition =             TransitionInflater.from(getContext())                     .inflateTransition(R.transition.image_shared_element_transition);     setSharedElementEnterTransition(transition);     // A similar mapping is set at the GridFragment with a setExitSharedElementCallback.     setEnterSharedElementCallback(             new SharedElementCallback() {                 @Override                 public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {                     // Locate the image view at the primary fragment (the ImageFragment that is currently                     // visible). To locate the fragment, call instantiateItem with the selection position.                     // At this stage, the method will simply return the fragment at the position and will                     // not create a new one.                     Fragment currentFragment = (Fragment) viewPager.getAdapter()                             .instantiateItem(viewPager, MainActivity.currentPosition);                     View view = currentFragment.getView();                     if (view == null) {                         return;                     }                     // Map the first shared element name to the child ImageView.                     sharedElements.put(names.get(0), view.findViewById(R.id.image));                 }             }); } | 
遅延遷移をさせる
サンプルでは、urlから画像をloadする際など、
画像が読み込めていない場合のためにpostponeEnterTransition()
により、遅延遷移の設定をしています。
postponeEnterTransition() を読んだ場合に遷移をスタートさせるためには、
startPostponedEnterTransition()を呼びます。
表示されるGrid Imageの1つ1つはFragmentを継承したImageFragmentクラスになっています。
ImageFragmentクラスのonCreateViewでGlideを使って画像を読み込み
読み込みが終了したことを知らせるCallback関数onResouceReady()
または、読み込みが失敗した時に呼ばれるonLoadFailed()で
startPostponedEnterTransition()を呼ぶことにより遅延遷移を実装しています。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,   @Nullable Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_image, container, false); Bundle arguments = getArguments(); @DrawableRes int imageRes = arguments.getInt(KEY_IMAGE_RES); // Just like we do when binding views at the grid, we set the transition name to be the string // value of the image res. view.findViewById(R.id.image).setTransitionName(String.valueOf(imageRes)); // Load the image with Glide to prevent OOM error when the image drawables are very large. Glide.with(this)     .load(imageRes)     .listener(new RequestListener<Drawable>() {       @Override       public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable>           target, boolean isFirstResource) {         // The postponeEnterTransition is called on the parent ImagePagerFragment, so the         // startPostponedEnterTransition() should also be called on it to get the transition         // going in case of a failure.         getParentFragment().startPostponedEnterTransition();         return false;       }       @Override       public boolean onResourceReady(Drawable resource, Object model, Target<Drawable>           target, DataSource dataSource, boolean isFirstResource) {         // The postponeEnterTransition is called on the parent ImagePagerFragment, so the         // startPostponedEnterTransition() should also be called on it to get the transition         // going when the image is ready.         getParentFragment().startPostponedEnterTransition();         return false;       }     })     .into((ImageView) view.findViewById(R.id.image)); return view; } | 
sharedElementを使った遷移
RecyclerView.Adapterクラスを継承したGridAdapterクラス内にて、
clickListenerをセットしsharedElementを使った遷移をおこなっています。
まず、ViewPagerからバックキーを押して戻った際にsharedElementを用いた遷移を
行えるように、Activityの位置を保存するためのstatic変数に位置を代入します。
続いて、アニメーションの対象元から自身を除くために、excludeTarget()を呼びます。
最後にFragmentを切り替える際にaddSharedElement()を呼び、sharedElementの設定をし、
commit()を実行します。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |   /**    * Handles a view click by setting the current position to the given {@code position} and    * starting a {@link  ImagePagerFragment} which displays the image at the position.    *    * @param view the clicked {@link ImageView} (the shared element view will be re-mapped at the    * GridFragment's SharedElementCallback)    * @param position the selected view position    */   @Override   public void onItemClicked(View view, int position) {     // Update the position.     MainActivity.currentPosition = position;     // Exclude the clicked card from the exit transition (e.g. the card will disappear immediately     // instead of fading out with the rest to prevent an overlapping animation of fade and move).     ((TransitionSet) fragment.getExitTransition()).excludeTarget(view, true);     ImageView transitioningView = view.findViewById(R.id.card_image);     fragment.getFragmentManager()         .beginTransaction()         .setReorderingAllowed(true) // Optimize for shared element transition         .addSharedElement(transitioningView, transitioningView.getTransitionName())         .replace(R.id.fragment_container, new ImagePagerFragment(), ImagePagerFragment.class             .getSimpleName())         .addToBackStack(null)         .commit();   } | 
addSharedFragment
| sharedElement | View: A View in a disappearing Fragment to match with a View in an appearing Fragment. | 
| name | String: The transitionName for a View in an appearing Fragment to match to the shared element. | 
遷移対象となるnameは、onBind関数で設定しています。
| 1 2 3 4 5 6 |   void onBind() {     int adapterPosition = getAdapterPosition();     setImage(adapterPosition);     // Set the string value of the image resource as the unique transition name for the view.     image.setTransitionName(String.valueOf(IMAGE_DRAWABLES[adapterPosition]));   } | 
PageViewから戻った際にスクロールポジションを合わせる
GridFragmentのonCreatedViewにて、LayoutChangeListenerをセットし、
Activityに保存していたポジションに戻すことでスクロール位置をPageViewで
選択されたイメージの位置に移動させています。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {     super.onViewCreated(view, savedInstanceState);     scrollToPosition(); } /**  * Scrolls the recycler view to show the last viewed item in the grid. This is important when  * navigating back from the grid.  */ private void scrollToPosition() {     recyclerView.addOnLayoutChangeListener(new OnLayoutChangeListener() {         @Override         public void onLayoutChange(View v,                                    int left,                                    int top,                                    int right,                                    int bottom,                                    int oldLeft,                                    int oldTop,                                    int oldRight,                                    int oldBottom) {             recyclerView.removeOnLayoutChangeListener(this);             final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();             View viewAtPosition = layoutManager.findViewByPosition(MainActivity.currentPosition);             // Scroll to position if the view for the current position is null (not currently part of             // layout manager children), or it's not completely visible.             if (viewAtPosition == null || layoutManager                     .isViewPartiallyVisible(viewAtPosition, false, true)) {                 recyclerView.post(() -> layoutManager.scrollToPosition(MainActivity.currentPosition));             }         }     }); } | 
以上になります。
全体のsourceはdeveloper pageのgitを参照ください。
clone先
https://github.com/google/android-transition-examples.git

codemore code
~~~~