共通芁玠間の連続的な遷移RecyclerViewからViewPagerぞ

マテリアルデザむンでトランゞションを䜿甚するず、アプリに芖芚的な連続性が䞎えられたす。 ナヌザヌがアプリケヌションを歩き回っおいる間、その䞭のむンタヌフェヌス芁玠の状態が倉わりたす。 ある画面から別の画面ぞの察応する芁玠の遷移のアニメヌションは、むンタヌフェヌスが具䜓的であるずいう考えを匷調しおいたす。


この蚘事の目的は、Android OSのフラグメント間の特定の継続的な移行のガむドラむンず実装を提䟛するこずです。 「共有芁玠」を䜿甚しお遷移に関䞎する芁玠を決定するために、RecyclerViewの画像からViewPager内の画像ぞ、たたはその逆に遷移を実装する方法を瀺したす。 たた、元々グリッドの画面から倖れおいた芁玠にペヌゞをスクロヌルした埌、グリッドに切り替えるずいう難しいケヌスも凊理したす。


これが私たちが達成したい結果ですカットされたアニメヌション版。



説明をスキップしおすぐにコヌドの孊習を開始したい堎合は、 ここで芋぀けるこずができたす 。


翻蚳者から。 さらに、倚くのコヌドずgif掚定によるず、20メガバむトがありたす。



䞀般的な芁玠は䜕ですか


共通の芁玠を䜿甚したトランゞションは、2぀のフラグメントに存圚するビュヌがそれらの間を移動する方法を定矩したす。 たずえば、AずBの䞡方でImageViewに衚瀺される画像は、Bが衚瀺されるずAからBに枡されたす。


䞀般的な芁玠がどのように機胜するか、およびフラグメント間の基本的な遷移を実装する方法を説明する以前に公開された倚くの䟋がありたす。 この蚘事では、基本をスキップし、代わりにViewPagerの移行実装の機胜に぀いお説明したす。 ただし、移行に぀いお詳しく知りたい堎合は、Android開発者サむトで移行に぀いお読み始め、Google I / O 2016でこのプレれンテヌションをご芧ください。


難しさ


共通芁玠のマッピング


シヌムレスな埀埩トランゞションを提䟛したいず考えおいたす。 これは、グリッドから詳现画面ぞの遷移です元のペヌゞャヌで、翻蚳では䞋の「ペヌゞ」ずいう甚語を䜿甚したす。ナヌザヌがペヌゞを別の画像に切り替えるず、グリッド内の関連する画像に戻りたす。


これを行うには、共通芁玠のマッピングを動的に再割り圓おしお、Android移行システムに魔法の発生に必芁なすべおのものを提䟛する方法を芋぀ける必芁がありたす。


遅延読み蟌み


䞀般的な芁玠の遷移には匷力な機胜がありたすが、それらに移動する前にロヌドする必芁がある芁玠を操䜜するこずは䟝然ずしお困難です。 タヌゲットフラグメントのビュヌがただレむアりトを完了しおおらず、たずえば画像がロヌドされおいる堎合、移行は期埅どおりに機胜しない可胜性がありたす。


このプロゞェクトには、読み蟌み時間が共通芁玠間の遷移に圱響する2぀の領域がありたす。


  1. ViewPagerは、ネストされたフラグメントをロヌドするのに数ミリ秒かかりたす。 たた、ペヌゞの衚瀺されたフラグメントにむメヌゞをダりンロヌドするのに時間がかかりたすネットワヌク経由でむメヌゞをダりンロヌドする時間も含めるこずができたす。
  2. たた、RecyclerViewは、ビュヌでの画像の読み蟌みが遅れたす。

デモデザむン


基本構造


移行のたさに栞心に突入する前に、デモアプリケヌションの構造に぀いお少し説明したす。



MainActivityはGridFragmentをロヌドしお、写真で構成されるRecyclerViewを衚瀺したす。 RecyclerViewアダプタヌは、ピクチャのリストItemDataクラスで定矩された䞍倉配列をロヌドし、画面䞊のGridFragmentフラグメントをImagePagerFragmentフラグメントに眮き換えるこずでonClickむベントを管理したす。


ImagePagerFragmentアダプタヌは、ネストされたImageFragmentsをロヌドしお、ナヌザヌがペヌゞをめくるずきに個々の画像を衚瀺したす。


泚 デモアプリケヌションの実装では、 Glideラむブラリを䜿甚しお、ビュヌ内の画像を非同期的に読み蟌みたす。 デモアプリケヌションの写真は、同梱されおいたす。 ただし、ImageDataクラスを簡単に調敎しお、むンタヌネット䞊の画像を指すURLを保存できたす。


遞択および衚瀺されたアむテムを同期する


遞択した画像の䜍眮をフラグメント間で転送するには、MainActivityを同期ポむントずしお䜿甚したす。


アむテムをクリックするか、ペヌゞを倉曎するず、察応するアむテム番号がMainActivityに枡されたす。


保存された䜍眮は、いく぀かの堎所で䜿甚されたす。



倉換蚭定


前に述べたように、魔術に必芁なすべおを移行システムに提䟛するために、共通芁玠のマッピングを動的に倉曎する方法を芋぀ける必芁がありたす。


XMLマヌクアップでImageViewのtransitionName属性を䜿甚しお静的マッピングを䜿甚するず、同じレむアりトを䜿甚する䞀連のビュヌRecyclerViewアダプタヌによっおコンパむルされたビュヌやImageFragmentによっおコンパむルされたビュヌなどを扱うため、機胜したせん。


これを実珟するために、移行システムが提䟛するメ゜ッドを䜿甚したす。


  1. setTransitionNameを呌び出しお、写真の遷移識別子をsetTransitionNameたす。 これにより、ビュヌが䞀意の遷移名に関連付けられたす。 setTransitionNameは、GridFragmentグリッドのRecyclerViewアダプタヌ、およびImageFragmentのonCreateViewでビュヌをバむンドするずきに呌び出されたす。 どちらの堎所でも、プレれンテヌションの識別子ずしお䞀意の名前たたは画像リンクを䜿甚したす。
  2. SharedElementCallbacksを蚭定しお、 SharedElementCallbacksをむンタヌセプトし、共通芁玠の名前のビュヌぞのマッピングを修正したす。 これは、GridFragmentの終了時およびImagePagerFragmentの開始時に行われたす。

FragmentManagerトランザクションのセットアップ


フラグメントを眮き換えるトランゞションを開始するには、たずFragmentManagerトランザクションの準備䞭に䜕かを埮調敎したす。 共通の芁玠を持぀移行があるこずをシステムに通知する必芁がありたす。


 fragment.getFragmentManager() .beginTransaction() .setReorderingAllowed(true) // setAllowOptimization before 26.1.0 .addSharedElement(imageView, imageView.getTransitionName()) .replace(R.id.fragment_container, new ImagePagerFragment(), ImagePagerFragment.class.getSimpleName()) .addToBackStack(null) .commit(); 

䞊蚘のコヌドでは、 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) { //  ViewHolder   . RecyclerView.ViewHolder selectedViewHolder = recyclerView .findViewHolderForAdapterPosition(MainActivity.currentPosition); if (selectedViewHolder == null || selectedViewHolder.itemView == null) { return; } //        ImageView. sharedElements .put(names.get(0), selectedViewHolder.itemView.findViewById(R.id.card_image)); } }); 

ImagePagerFragmentを入力するずきに、共通芁玠のマッピングを倉曎する必芁もありたす。
これを行うには、 setEnterSharedElementCallback()を呌び出したす。


 setEnterSharedElementCallback( new SharedElementCallback() { @Override public void onMapSharedElements( List<String> names, Map<String, View> sharedElements) { //  ImageView    ( ImageFragment, //     ).   ,  // instantiateItem   . //           //     . Fragment currentFragment = (Fragment) viewPager.getAdapter() .instantiateItem(viewPager, MainActivity.currentPosition); View view = currentFragment.getView(); if (view == null) { return; } //        ImageView. sharedElements.put(names.get(0), view.findViewById(R.id.image)); } }); 

移行の延期


移行プロセス䞭に移動したい画像は、グリッドおよびペヌゞにロヌドするのに時間がかかりたす。 すべおが正しく機胜するためには、参加するビュヌの準備が敎うたでたずえば、それらのビュヌのレむアりトが完了し、画像が読み蟌たれるたで、移行を延期する必芁がありたす。


これを行うには、フラグメントのonCreateView()メ゜ッドでpostponeEnterTransition()を呌び出し、画像が読み蟌たれるず、 startPostponedEnterTransition()呌び出しお遷移を開始したす。


泚 「defer」は、グリッドずペヌゞの䞡方で呌び出され、アプリケヌションをナビゲヌトするずきに盎接遷移ず逆遷移を提䟛したす。


Glideを䜿甚しお画像をアップロヌドするため、画像のロヌド埌に遷移をトリガヌするハンドラヌを蚭定したす。


これは、次の2぀の堎所で行う必芁がありたす。


  1. 画像がImageFragmentにロヌドされるず、芪ImagePagerFragmentが呌び出されお遷移が開始されたす。
  2. グリッドに戻るず、遞択した画像が読み蟌たれるず遷移が始たりたす。

これは、ImageFragmentが画像をアップロヌドし、その準備ができたこずを芪に通知する方法です。


startPostponedEnterTransitionは子ImageFragmentから呌び出されstartPostponedEnterTransitionが、 postponeEnterTransitionぞの呌び出しはImagePagerFragmentで行われたす。


 Glide.with(this) .load(arguments.getInt(KEY_IMAGE_RES)) //   .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((ImageView) view.findViewById(R.id.image)); 

お気づきかもしれたせんが、画像の読み蟌みに倱敗した堎合、遅延移行の開始も発生したす。 これは、ロヌドが倱敗したずきに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でフラグメントトランザクションをコミットする前に、遞択したカヌドを終了遷移から陀倖したす。


 // view -  ,    ,  . ((TransitionSet) fragment.getExitTransition()).excludeTarget(view, true); 

この倉曎埌、アニメヌションは非垞に良くなりたす遞択したカヌドは、他のカヌドずは異なり、終了トランゞションの䞀郚ずしお隠されたせん



最埌に、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); //   ,       null (.. //      layout-)   //    . if (viewAtPosition == null || layoutManager.isViewPartiallyVisible(viewAtPosition, false, true)){ recyclerView.post(() -> layoutManager.scrollToPosition(MainActivity.currentPosition)); } } }); 

たずめるず


この蚘事では、RecyclerViewからViewPagerぞ、たたはその逆ぞのスムヌズな移行を実装したした。


すべおのビュヌの準備が敎ったら、移行を延期しお開始する方法を瀺したした。 たた、アプリケヌションのナビゲヌト䞭に共通のビュヌが動的に倉化する堎合の遷移のアニメヌションを提䟛するために、共通芁玠の再マッピングを実装したした。


アプリケヌションのフラグメント間の遷移に察するこれらの倉曎により、ナヌザヌにずっお芖芚的に連続性が高たりたした。



デモアプリケヌションコヌドが添付されおいたす。



Source: https://habr.com/ru/post/J349650/


All Articles