ãããªã¢ã«ãã¶ã€ã³ã§ãã©ã³ãžã·ã§ã³ã䜿çšãããšãã¢ããªã«èŠèŠçãªé£ç¶æ§ãäžããããŸãã ãŠãŒã¶ãŒãã¢ããªã±ãŒã·ã§ã³ãæ©ãåã£ãŠããéããã®äžã®ã€ã³ã¿ãŒãã§ãŒã¹èŠçŽ ã®ç¶æ
ãå€ãããŸãã ããç»é¢ããå¥ã®ç»é¢ãžã®å¯Ÿå¿ããèŠçŽ ã®é·ç§»ã®ã¢ãã¡ãŒã·ã§ã³ã¯ãã€ã³ã¿ãŒãã§ãŒã¹ãå
·äœçã§ãããšããèãã匷調ããŠããŸãã
ãã®èšäºã®ç®çã¯ãAndroid OSã®ãã©ã°ã¡ã³ãéã®ç¹å®ã®ç¶ç¶çãªç§»è¡ã®ã¬ã€ãã©ã€ã³ãšå®è£
ãæäŸããããšã§ãã ãå
±æèŠçŽ ãã䜿çšããŠé·ç§»ã«é¢äžããèŠçŽ ã決å®ããããã«ãRecyclerViewã®ç»åããViewPagerå
ã®ç»åãžããŸãã¯ãã®éã«é·ç§»ãå®è£
ããæ¹æ³ã瀺ããŸãã ãŸããå
ã
ã°ãªããã®ç»é¢ããå€ããŠããèŠçŽ ã«ããŒãžãã¹ã¯ããŒã«ããåŸãã°ãªããã«åãæ¿ãããšããé£ããã±ãŒã¹ãåŠçããŸãã
ãããç§ãã¡ãéæãããçµæã§ãïŒã«ãããããã¢ãã¡ãŒã·ã§ã³çïŒã
説æãã¹ãããããŠããã«ã³ãŒãã®åŠç¿ãéå§ãããå Žåã¯ã ããã§èŠã€ããããšãã§ããŸã ã
翻蚳è
ããã ããã«ãå€ãã®ã³ãŒããšgifïŒæšå®ã«ãããšã20ã¡ã¬ãã€ãïŒããããŸãã

äžè¬çãªèŠçŽ ã¯äœã§ããïŒ
å
±éã®èŠçŽ ã䜿çšãããã©ã³ãžã·ã§ã³ã¯ã2ã€ã®ãã©ã°ã¡ã³ãã«ååšãããã¥ãŒããããã®éã移åããæ¹æ³ãå®çŸ©ããŸãã ããšãã°ãAãšBã®äž¡æ¹ã§ImageViewã«è¡šç€ºãããç»åã¯ãBã衚瀺ããããšAããBã«æž¡ãããŸãã
äžè¬çãªèŠçŽ ãã©ã®ããã«æ©èœããããããã³ãã©ã°ã¡ã³ãéã®åºæ¬çãªé·ç§»ãå®è£
ããæ¹æ³ã説æãã以åã«å
¬éãããå€ãã®äŸããããŸãã ãã®èšäºã§ã¯ãåºæ¬ãã¹ããããã代ããã«ViewPagerã®ç§»è¡å®è£
ã®æ©èœã«ã€ããŠèª¬æããŸãã ãã ãã移è¡ã«ã€ããŠè©³ããç¥ãããå Žåã¯ãAndroidéçºè
ãµã€ãã§ç§»è¡ã«ã€ããŠèªã¿å§ããGoogle I / O 2016ã§ãã®ãã¬ãŒã³ããŒã·ã§ã³ãã芧ãã ããã
é£ãã
å
±éèŠçŽ ã®ãããã³ã°
ã·ãŒã ã¬ã¹ãªåŸåŸ©ãã©ã³ãžã·ã§ã³ãæäŸããããšèããŠããŸãã ããã¯ãã°ãªãããã詳现ç»é¢ãžã®é·ç§»ã§ãïŒå
ã®ããŒãžã£ãŒã§ã翻蚳ã§ã¯äžã®ãããŒãžããšããçšèªã䜿çšããŸãïŒããŠãŒã¶ãŒãããŒãžãå¥ã®ç»åã«åãæ¿ãããšãã°ãªããå
ã®é¢é£ããç»åã«æ»ããŸãã
ãããè¡ãã«ã¯ãå
±éèŠçŽ ã®ãããã³ã°ãåçã«åå²ãåœãŠããŠãAndroid移è¡ã·ã¹ãã ã«éæ³ã®çºçã«å¿
èŠãªãã¹ãŠã®ãã®ãæäŸããæ¹æ³ãèŠã€ããå¿
èŠããããŸãã
é
延èªã¿èŸŒã¿
äžè¬çãªèŠçŽ ã®é·ç§»ã«ã¯åŒ·åãªæ©èœããããŸããããããã«ç§»åããåã«ããŒãããå¿
èŠãããèŠçŽ ãæäœããããšã¯äŸç¶ãšããŠå°é£ã§ãã ã¿ãŒã²ãããã©ã°ã¡ã³ãã®ãã¥ãŒããŸã ã¬ã€ã¢ãŠããå®äºããŠããããããšãã°ç»åãããŒããããŠããå Žåã移è¡ã¯æåŸ
ã©ããã«æ©èœããªãå¯èœæ§ããããŸãã
ãã®ãããžã§ã¯ãã«ã¯ãèªã¿èŸŒã¿æéãå
±éèŠçŽ éã®é·ç§»ã«åœ±é¿ãã2ã€ã®é åããããŸãã
- ViewPagerã¯ããã¹ãããããã©ã°ã¡ã³ããããŒãããã®ã«æ°ããªç§ããããŸãã ãŸããããŒãžã®è¡šç€ºããããã©ã°ã¡ã³ãã«ã€ã¡ãŒãžãããŠã³ããŒãããã®ã«æéãããããŸãïŒãããã¯ãŒã¯çµç±ã§ã€ã¡ãŒãžãããŠã³ããŒãããæéãå«ããããšãã§ããŸãïŒã
- ãŸããRecyclerViewã¯ããã¥ãŒã§ã®ç»åã®èªã¿èŸŒã¿ãé
ããŸãã
ãã¢ãã¶ã€ã³
åºæ¬æ§é
移è¡ã®ãŸãã«æ žå¿ã«çªå
¥ããåã«ããã¢ã¢ããªã±ãŒã·ã§ã³ã®æ§é ã«ã€ããŠå°ã説æããŸãã

MainActivityã¯GridFragmentãããŒãããŠãåçã§æ§æãããRecyclerViewã衚瀺ããŸãã RecyclerViewã¢ããã¿ãŒã¯ããã¯ãã£ã®ãªã¹ãïŒItemDataã¯ã©ã¹ã§å®çŸ©ãããäžå€é
åïŒãããŒãããç»é¢äžã®GridFragmentãã©ã°ã¡ã³ããImagePagerFragmentãã©ã°ã¡ã³ãã«çœ®ãæããããšã§onClickã€ãã³ãã管çããŸãã
ImagePagerFragmentã¢ããã¿ãŒã¯ããã¹ããããImageFragmentsãããŒãããŠããŠãŒã¶ãŒãããŒãžãããããšãã«åã
ã®ç»åã衚瀺ããŸãã
泚 ïŒãã¢ã¢ããªã±ãŒã·ã§ã³ã®å®è£
ã§ã¯ã Glideã©ã€ãã©ãªã䜿çšããŠããã¥ãŒå
ã®ç»åãéåæçã«èªã¿èŸŒã¿ãŸãã ãã¢ã¢ããªã±ãŒã·ã§ã³ã®åçã¯ãå梱ãããŠããŸãã ãã ããImageDataã¯ã©ã¹ãç°¡åã«èª¿æŽããŠãã€ã³ã¿ãŒãããäžã®ç»åãæãURLãä¿åã§ããŸãã
éžæããã³è¡šç€ºãããã¢ã€ãã ãåæãã
éžæããç»åã®äœçœ®ããã©ã°ã¡ã³ãéã§è»¢éããã«ã¯ãMainActivityãåæãã€ã³ããšããŠäœ¿çšããŸãã
ã¢ã€ãã ãã¯ãªãã¯ããããããŒãžãå€æŽãããšã察å¿ããã¢ã€ãã çªå·ãMainActivityã«æž¡ãããŸãã
ä¿åãããäœçœ®ã¯ãããã€ãã®å Žæã§äœ¿çšãããŸãã
- ViewPagerã«è¡šç€ºããããŒãžã決å®ããŸãã
- ã°ãªããã«æ»ããéžæããã¢ã€ãã ã衚瀺ãããããšã確èªããäœçœ®ã«èªåã¹ã¯ããŒã«ãããšãã
- ãããŠãã¡ããã次ã®ã»ã¯ã·ã§ã³ã§èŠãããã«ãé·ç§»ãã³ãã©ããã€ã³ãããŸãã
å€æèšå®
åã«è¿°ã¹ãããã«ãéè¡ã«å¿
èŠãªãã¹ãŠã移è¡ã·ã¹ãã ã«æäŸããããã«ãå
±éèŠçŽ ã®ãããã³ã°ãåçã«å€æŽããæ¹æ³ãèŠã€ããå¿
èŠããããŸãã
XMLããŒã¯ã¢ããã§ImageViewã®transitionName
å±æ§ã䜿çšããŠéçãããã³ã°ã䜿çšãããšãåãã¬ã€ã¢ãŠãã䜿çšããäžé£ã®ãã¥ãŒïŒRecyclerViewã¢ããã¿ãŒã«ãã£ãŠã³ã³ãã€ã«ããããã¥ãŒãImageFragmentã«ãã£ãŠã³ã³ãã€ã«ããããã¥ãŒãªã©ïŒãæ±ããããæ©èœããŸããã
ãããå®çŸããããã«ã移è¡ã·ã¹ãã ãæäŸããã¡ãœããã䜿çšããŸãã
setTransitionName
ãåŒã³åºããŠãåçã®é·ç§»èå¥åãsetTransitionName
ãŸãã ããã«ããããã¥ãŒãäžæã®é·ç§»åã«é¢é£ä»ããããŸãã setTransitionName
ã¯ãGridFragmentã°ãªããã®RecyclerViewã¢ããã¿ãŒãããã³ImageFragmentã®onCreateViewã§ãã¥ãŒããã€ã³ããããšãã«åŒã³åºãããŸãã ã©ã¡ãã®å Žæã§ãããã¬ãŒã³ããŒã·ã§ã³ã®èå¥åãšããŠäžæã®ååãŸãã¯ç»åãªã³ã¯ã䜿çšããŸããSharedElementCallbacks
ãèšå®ããŠã SharedElementCallbacks
ãã€ã³ã¿ãŒã»ããããå
±éèŠçŽ ã®ååã®ãã¥ãŒãžã®ãããã³ã°ãä¿®æ£ããŸãã ããã¯ãGridFragmentã®çµäºæããã³ImagePagerFragmentã®éå§æã«è¡ãããŸãã
FragmentManagerãã©ã³ã¶ã¯ã·ã§ã³ã®ã»ããã¢ãã
ãã©ã°ã¡ã³ãã眮ãæãããã©ã³ãžã·ã§ã³ãéå§ããã«ã¯ããŸãFragmentManagerãã©ã³ã¶ã¯ã·ã§ã³ã®æºåäžã«äœãã埮調æŽããŸãã å
±éã®èŠçŽ ãæã€ç§»è¡ãããããšãã·ã¹ãã ã«éç¥ããå¿
èŠããããŸãã
fragment.getFragmentManager() .beginTransaction() .setReorderingAllowed(true)
äžèšã®ã³ãŒãã§ã¯ã setReorderingAllowed
true
setReorderingAllowed
ãŠãtrue
ã ããã«ããããã©ã°ã¡ã³ãã®ç¶æ
å€æŽã®é åºãå€æŽããŠãé·ç§»ã®å€èŠ³ãè¯ãããããšãã§ããŸãã è¿œå ããããã©ã°ã¡ã³ãã«ã€ããŠã¯ã onDestroy
åé€ããããã©ã°ã¡ã³ãã§onDestroy
ããåã«onCreate(Bundle)
onDestroy
ããŸããããã«ããã移è¡ãå§ãŸãåã«å
±éã®ã€ã³ã¿ãŒãã§ã€ã¹èŠçŽ ãäœæããŠé
眮ã§ããŸãã
移è¡ã®åç
é·ç§»ã¢ãã¡ãŒã·ã§ã³äžã«ç»åãæ°ããå Žæã«ã©ã®ããã«å€æãããããå€æããããã«ãXMLãã¡ã€ã«ã«TransitionSetãã»ããã¢ããããImagePagerFragmentã«ããŒãããŸãã
<ImagePagerFragment.java>
Transition transition = TransitionInflater.from(getContext()) .inflateTransition(R.transition.image_shared_element_transition); setSharedElementEnterTransition(transition);
<image_shared_element_transition.xml>
<?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:duration="375" android:interpolator="@android:interpolator/fast_out_slow_in" android:transitionOrdering="together"> <changeClipBounds/> <changeTransform/> <changeBounds/> </transitionSet>
äžèŽããå
±éèŠçŽ ãå€æŽãã
GridFragmentãçµäºãããšãã®å€æŽããå§ããŸãããã ãããè¡ãã«ã¯ã setExitSharedElementCallback()
ãåŒã³åºããŠã SharedElementCallback
ãã©ã³ãžã·ã§ã³åãšãã¥ãŒã«äžèŽããSharedElementCallback
ãæž¡ããŸãã
ãã®ãã³ãã©ã¯ããã©ã°ã¡ã³ããã©ã³ã¶ã¯ã·ã§ã³ã®åŠçäžã«ãã©ã°ã¡ã³ãããåŒã³åºãããšããããã³ããã¯ã¹ã¿ãã¯ããããã·ã¥ããããšãã«ãã©ã°ã¡ã³ããå
¥åãããšãã«ïŒéããã²ãŒã·ã§ã³äžã«ïŒåŒã³åºãããããšã«æ³šæããããšãéèŠã§ãã ãã®åäœã䜿çšããŠãäžè¬çãªãã¥ãŒã®å¯Ÿå¿ããããã³ã°ããç»åã®ããããŒãžãããã£ãåŸã«ãã¥ãŒãå€æŽãããå Žåã«å¯Ÿå¿ã§ããããã«é·ç§»ã調æŽããŸãã
ãã®ç¹æ®ãªã±ãŒã¹ã§ã¯ãã°ãªããããViewPagerã衚瀺ãããã©ã°ã¡ã³ãã«1ã€ã®ImageViewã移åããã ããªã®ã§ã onMapSharedElements
ãã³ãã©ãŒã§åãåã£ãæåã®ååä»ãèŠçŽ ã®ãããã³ã°ãå€æŽããã ãã§ãã
<GridFragment.java>
setExitSharedElementCallback( new SharedElementCallback() { @Override public void onMapSharedElements( List<String> names, Map<String, View> sharedElements) {
ImagePagerFragmentãå
¥åãããšãã«ãå
±éèŠçŽ ã®ãããã³ã°ãå€æŽããå¿
èŠããããŸãã
ãããè¡ãã«ã¯ã setEnterSharedElementCallback()
ãåŒã³åºããŸãã
setEnterSharedElementCallback( new SharedElementCallback() { @Override public void onMapSharedElements( List<String> names, Map<String, View> sharedElements) {
移è¡ã®å»¶æ
移è¡ããã»ã¹äžã«ç§»åãããç»åã¯ãã°ãªããããã³ããŒãžã«ããŒãããã®ã«æéãããããŸãã ãã¹ãŠãæ£ããæ©èœããããã«ã¯ãåå ãããã¥ãŒã®æºåãæŽããŸã§ïŒããšãã°ããããã®ãã¥ãŒã®ã¬ã€ã¢ãŠããå®äºããç»åãèªã¿èŸŒãŸãããŸã§ïŒã移è¡ã延æããå¿
èŠããããŸãã
ãããè¡ãã«ã¯ããã©ã°ã¡ã³ãã®onCreateView()
ã¡ãœããã§postponeEnterTransition()
ãåŒã³åºããç»åãèªã¿èŸŒãŸãããšã startPostponedEnterTransition()
åŒã³åºããŠé·ç§»ãéå§ããŸãã
泚 ïŒãdeferãã¯ãã°ãªãããšããŒãžã®äž¡æ¹ã§åŒã³åºãããã¢ããªã±ãŒã·ã§ã³ãããã²ãŒããããšãã«çŽæ¥é·ç§»ãšéé·ç§»ãæäŸããŸãã
Glideã䜿çšããŠç»åãã¢ããããŒããããããç»åã®ããŒãåŸã«é·ç§»ãããªã¬ãŒãããã³ãã©ãŒãèšå®ããŸãã
ããã¯ã次ã®2ã€ã®å Žæã§è¡ãå¿
èŠããããŸãã
- ç»åãImageFragmentã«ããŒãããããšã芪ImagePagerFragmentãåŒã³åºãããŠé·ç§»ãéå§ãããŸãã
- ã°ãªããã«æ»ããšãéžæããç»åãèªã¿èŸŒãŸãããšé·ç§»ãå§ãŸããŸãã
ããã¯ãImageFragmentãç»åãã¢ããããŒããããã®æºåãã§ããããšã芪ã«éç¥ããæ¹æ³ã§ãã
startPostponedEnterTransition
ã¯åImageFragmentããåŒã³åºããstartPostponedEnterTransition
ãã postponeEnterTransition
ãžã®åŒã³åºãã¯ImagePagerFragmentã§è¡ãããŸãã
Glide.with(this) .load(arguments.getInt(KEY_IMAGE_RES))
ãæ°ã¥ããããããŸããããç»åã®èªã¿èŸŒã¿ã«å€±æããå Žåãé
延移è¡ã®éå§ãçºçããŸãã ããã¯ãããŒãã倱æãããšãã«UIãããªãŒãºããã®ãé²ãããã§ãã
æåŸã®ä»äžã
é·ç§»ãããã«ã¹ã ãŒãºã«ããããã«ãç»åãããŒãžç»é¢ã«ç§»åãããšãã«ã°ãªããèŠçŽ ãæãããããšæããŸãã
ãããå®çŸããã«ã¯ãTransitionSetãäœæããGridFragmentãçµäºãããã©ã³ãžã·ã§ã³ãšããŠäœ¿çšããŸãã
setExitTransition(TransitionInflater.from(getContext()) .inflateTransition(R.transition.grid_exit_transition));
<?xml version="1.0" encoding="utf-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>
åºåã¢ãã¡ãŒã·ã§ã³ãèšå®ããåŸã®é·ç§»ã¯æ¬¡ã®ããã«ãªããŸãã

ãæ°ã¥ããããããŸãããã移è¡ã¯ãŸã å®å
šã«ã¯æŽç·ŽãããŠããŸããã 調å
ã¢ãã¡ãŒã·ã§ã³ã¯ãããŒãžã«ç§»åããç»åãå«ããã®ãå«ãããã¹ãŠã®ã°ãªããã«ãŒãã«å¯ŸããŠå®è¡ãããŸãã
ãããä¿®æ£ããããã«ãGridAdapterã§ãã©ã°ã¡ã³ããã©ã³ã¶ã¯ã·ã§ã³ãã³ãããããåã«ãéžæããã«ãŒããçµäºé·ç§»ããé€å€ããŸãã
ãã®å€æŽåŸãã¢ãã¡ãŒã·ã§ã³ã¯éåžžã«è¯ããªããŸãïŒéžæããã«ãŒãã¯ãä»ã®ã«ãŒããšã¯ç°ãªããçµäºãã©ã³ãžã·ã§ã³ã®äžéšãšããŠé ãããŸããïŒïŒ

æåŸã«ãGridFragmentãèšå®ããŠãããŒãžããæ»ããšãã«ã¹ã¯ããŒã«ããã«ãŒãã衚瀺ããŸãïŒããã¯onViewCreated
ã§è¡ããonViewCreated
ïŒã
recyclerView.addOnLayoutChangeListener( new OnLayoutChangeListener() { @Override public void onLayoutChange(View view, 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);
ãŸãšãããš
ãã®èšäºã§ã¯ãRecyclerViewããViewPagerãžããŸãã¯ãã®éãžã®ã¹ã ãŒãºãªç§»è¡ãå®è£
ããŸããã
ãã¹ãŠã®ãã¥ãŒã®æºåãæŽã£ããã移è¡ã延æããŠéå§ããæ¹æ³ã瀺ããŸããã ãŸããã¢ããªã±ãŒã·ã§ã³ã®ããã²ãŒãäžã«å
±éã®ãã¥ãŒãåçã«å€åããå Žåã®é·ç§»ã®ã¢ãã¡ãŒã·ã§ã³ãæäŸããããã«ãå
±éèŠçŽ ã®åãããã³ã°ãå®è£
ããŸããã
ã¢ããªã±ãŒã·ã§ã³ã®ãã©ã°ã¡ã³ãéã®é·ç§»ã«å¯Ÿãããããã®å€æŽã«ããããŠãŒã¶ãŒã«ãšã£ãŠèŠèŠçã«é£ç¶æ§ãé«ãŸããŸããã

ãã¢ã¢ããªã±ãŒã·ã§ã³ã³ãŒããæ·»ä»ãããŠããŸãã