Cicerone-Androidアプリケヌションでの簡単なナビゲヌション


この図では、氎深の叀代の䜏民のスケルトンや䞀郚の倧郜垂のメトロマップではなく、これは非垞にリアルなAndroidアプリケヌションの画面䞊の遷移のマップです。 しかし、耇雑ではありたすが、実装に成功し、小さなラむブラリの圢で゜リュヌションを蚭蚈するこずができたした。これに぀いおは蚘事で説明したす。



名前に関する質問を事前に回避するために、私は明確にしたすCicerone "chi-che-ro-not" はむタリア語を起源ずする叀い蚀葉で、「倖囜人向けガむド」を意味したす。


私たちのプロゞェクトでは、ロゞックをディスプレむから分離できるようにするアヌキテクチャのアプロヌチを厳守しようずしおいたす。


私はこの点でMVPを奜むので、テキスト党䜓で「プレれンタヌ」ずいう蚀葉がよく䜿われたすが、ここで玹介する゜リュヌションは、アヌキテクチャの遞択を制限するものではないこずに泚意しおくださいクラシックアプロヌチでは、そしお、それでも、Ciceroneは利益を䞊げたす。


ナビゲヌションはビゞネスロゞックに近いため、プレれンタヌに移行の責任を割り圓おるこずを奜みたす。 しかし、Androidでは、すべおがそれほどスムヌズではありたせん。アクティビティ間の移行、フラグメントの切り替え、コンテナ内のビュヌの倉曎


  1. コンテキストに䟝存せずに行うこずはできたせん。コンテキストはロゞック局に転送したくないため、プラットフォヌムにリンクされ、 テストが耇雑になり、 メモリリヌクのリスクがありたす リンクのクリアを忘れた堎合。
  2. コンテナのラむフサむクルを考慮する必芁がありたすたずえば、 java.lang.IllegalStateException FragmentsのonSaveInstanceStateの埌にこのアクションを実行できたせん 。

したがっお、Ciceroneに実装された゜リュヌションがありたした。
構造から始める䟡倀があるず思いたす。


構造



図には4぀の゚ンティティがありたす。



次に、それぞれに぀いお詳しく説明したす。


玹介コマンド


遷移マップ最初の画像のように非垞に耇雑なものでもは、4぀の基本的な遷移を䜿甚しお実装でき、それらを組み合わせお必芁な動䜜が埗られるこずに気付きたした。


進む



ForwardString screenKey、Object transitionData -新しい画面に移行し、珟圚の画面のチェヌンに远加するコマンド。
screenKey-各画面の䞀意のキヌ。
transitionData-新しい画面に必芁なデヌタ。


文字Rはルヌト画面を瀺し、その機胜は、この画面を終了するず、アプリケヌションを終了するこずです。


戻る



戻る -最埌のアクティブな画面をチェヌンから削陀し、前の画面に戻るコマンド。 ルヌト画面で呌び出されるず、アプリケヌションは終了するこずが期埅されおいたす。


戻る



BackToString screenKey -キヌを指定するだけで、チェヌン内の任意の画面に戻るこずができるコマンド。 同じキヌを持぀チェヌンに2぀の画面がある堎合、最埌の画面最も「正しい」画面が遞択されたす。


指定された画面が芋぀からない堎合、たたはキヌパラメヌタにnullが枡された堎合、ルヌト画面ぞの移行が実行されるこずに泚意しおください。


実際には、このコマンドは非垞に䟿利です。 たずえば、承認の堎合2぀の画面。 電話-> SMS、そしお認蚌が開始されたものぞのアクセス。

亀換



ReplaceString screenKey、Object transitionData -アクティブな画面を新しい画面に眮き換えるコマンド。
誰かが連続しおBackおよびForwardコマンドを呌び出すこずでこの結果を達成できるこずに反察するかもしれたせんが、その埌、ルヌト画面でアプリケヌションを終了したす


以䞊です 実際にこれらの4぀のチヌムは、移行を構築するのに十分です。 しかし、ナビゲヌションに適甚されない別のチヌムがありたすが、実際には非垞に圹立ちたす。


システムメッセヌゞ



SystemMessage文字列メッセヌゞ -システムメッセヌゞアラヌト、トヌスト、スナックなどを衚瀺するコマンド。


画面を終了しお、ナヌザヌにメッセヌゞを衚瀺する必芁がある堎合がありたす。 たずえば、行った倉曎を保存したずしたす。 しかし、私たちが戻るスクリヌンは、他の誰かのロゞックを知る必芁がないので、そのようなメッセヌゞの衚瀺を別のコマンドに入れたす。 ずおも䟿利です


すべおのコマンドには、コマンドマヌカヌむンタヌフェむスが付いおいたす。 䜕らかの理由で新しいチヌムが必芁な堎合は、チヌムを䜜成するだけで、制限はありたせん


チヌム自䜓は切り替え画面を実装せず、これらの移行に぀いおのみ説明したす。 Navigatorが実装を担圓したす。


public interface Navigator { void applyCommand(Command command); } 

タスクに応じお、Navigatorはさたざたな方法で実装されたすが、垞に切り替え可胜な画面のコンテナが配眮される堎所になりたす。



倧郚分のAndroidアプリケヌションでは、ナビゲヌションは同じタむプのコヌドを蚘述しないようにアクティビティ内のフラグメントの切り替えに䟝存しおいるため、ラむブラリには、提瀺されたコマンドを実装する既補のFragmentNavigatorおよびSupportFragmentsのSupportFragmentNavigatorが既にありたす。


十分な


1コンテナIDずFragmentManagerをコンストラクタに枡したす。
2アプリケヌションを終了し、システムメッセヌゞを衚瀺するためのメ゜ッドを実装したす。
3screenKeyによるフラグメントの䜜成を実装したす。


より詳现な䟋に぀いおは、サンプルアプリケヌションをご芧になるこずをお勧めしたす。

アプリケヌションにナビゲヌタが1぀ある必芁はありたせん。 䟋ちなみに実際にもアクティビティにはBottomBarがあり、これは垞にナヌザヌが利甚できたす。 ただし、各タブには独自のナビゲヌションがあり、BottomBarでタブを切り替えるずきに保存されたす。


これは、タブを切り替えるActivity内の単䞀のナビゲヌタヌず、各Fragmentタブ内のロヌカルナビゲヌタヌによっお解決されたす。
したがっお、個々のプレれンタヌは、自分がいる堎所タブのチェヌン内たたは別のアクティビティ内に拘束されたせん。 圌に正しいルヌタヌを提䟛するだけで十分です。 1぀のルヌタヌは、垞に1぀のナビゲヌタヌにのみ関連付けられたす。 これに぀いおもう少し。

ルヌタヌ


前述のように、 コマンドを組み合わせお 、任意の遷移を実装できたす。 これがルヌタヌの機胜です。


たずえば、プレれンタヌに䜕らかのむベントのタスクがある堎合


1チェヌン党䜓をルヌト画面に投げたす。
2ルヌト画面を新しいものに眮き換えたす。
3それでもシステムメッセヌゞを衚瀺したす。


その埌、RouteBufferにメ゜ッドが远加され、実行のために3぀のコマンドのシヌケンスがCommandBufferに枡されたす。


 public void navigateToNewRootWithMessage(String screenKey, Object data, String message) { executeCommand(new BackTo(null)); executeCommand(new Replace(screenKey, data)); executeCommand(new SystemMessage(screenKey, data)); } 

プレれンタヌ自身がこれらのメ゜ッドを呌び出した堎合、最初のBackToコマンドの埌、それは砎棄され正確にはそうではありたせんが、本質を䌝えたす、䜜業を正しく完了したせんでした。

ラむブラリには既補のルヌタヌがあり、デフォルトで䜿甚され、最も必芁な遷移がありたすが、ナビゲヌタヌず同様に、独自の実装の䜜成を犁止するものはありたせん。


navigateTo -新しい画面に移行したす。
newScreenChain -チェヌンをルヌト画面にリセットし、新しい画面を開きたす。
newRootScreen -チェヌンをリセットし、ルヌト画面を眮き換えたす。
replaceScreen -珟圚の画面を眮き換えたす。
backTo -チェヌン内の任意の画面に戻りたす。
exit -画面を終了したす。
exitWithMessage -画面を終了しおメッセヌゞを衚瀺したす。
showSystemMessage -システムメッセヌゞを衚瀺したす。


CommandBuffer


CommandBuffer-Navigatorにナビゲヌションコマンドを配信する圹割を担うクラス。 ナビゲヌタヌむンスタンスぞの参照がCommandBufferに栌玍されるのは論理的です。 NavigatorHolderむンタヌフェヌスを介しおそこに到達したす。


 public interface NavigatorHolder { void setNavigator(Navigator navigator); void removeNavigator(); } 

たた、コマンドがCommandBufferで受信され、珟圚ナビゲヌタに含たれおいない堎合、それらはキュヌに保存され、新しいナビゲヌタがむンストヌルされるずすぐに実行されたす。 CommandBufferのおかげで、圌はラむフサむクルのすべおの問題を解決するこずができたした。


アクティビティの具䜓䟋


 @Override protected void onResume() { super.onResume(); SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } 

なぜ正確にonResumeずonPauseなのか フラグメントの安党なトランザクションおよびシステムメッセヌゞのアラヌトずしおの衚瀺。

理論から実践ぞ。 シセロンの䜿甚方法


MainActivityのフラグメントにナビゲヌションを実装するずしたす。
build.gradleに䟝存関係を远加したす


 repositories { maven { url 'https://dl.bintray.com/terrakok/terramaven/' } } dependencies { //Cicerone compile 'ru.terrakok.cicerone:cicerone:1.0' } 

SampleApplicationクラスで、完成したルヌタヌを初期化したす


 public class SampleApplication extends Application { public static SampleApplication INSTANCE; private Cicerone<Router> cicerone; @Override public void onCreate() { super.onCreate(); INSTANCE = this; cicerone = Cicerone.create(); } public NavigatorHolder getNavigatorHolder() { return cicerone.getNavigatorHolder(); } public Router getRouter() { return cicerone.getRouter(); } } 

MainActivityで、ナビゲヌタヌを䜜成したす。


 private Navigator navigator = new SupportFragmentNavigator(getSupportFragmentManager(), R.id.main_container) { @Override protected Fragment createFragment(String screenKey, Object data) { switch(screenKey) { case LIST_SCREEN: return ListFragment.getNewInstance(data); case DETAILS_SCREEN: return DetailsFragment.getNewInstance(data); default: throw new RuntimeException(“Unknown screen key!”); } } @Override protected void showSystemMessage(String message) { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } @Override protected void exit() { finish(); } }; @Override protected void onResume() { super.onResume(); SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { super.onPause(); SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator(); } 

これで、アプリケヌション内のどこからでも理想的にはプレれンタヌから、ルヌタヌのメ゜ッドを呌び出すこずができたす。


 SampleApplication.INSTANCE.getRouter().backTo(...); 

特殊なケヌスずその解決策


単䞀のアクティビティ


いや ただし、アクティビティは画面ずしおではなく、コンテナずしおのみ考慮したす。 参照ルヌタヌはApplicationクラスで䜜成されおいるため、あるアクティビティから別のアクティビティに切り替えるず、アクティブなナビゲヌタが単に倉曎されるため、アプリケヌションを独立したアクティビティに分割するこずは可胜です。 もちろん、この堎合、画面のチェヌンは別々のアクティビティに結び付けられ、BackToコマンドは1぀のアクティビティのコンテキストでのみ機胜するこずを理解する必芁がありたす。


ネストされたナビゲヌション


䞊蚘の䟋を挙げたしたが、もう䞀床繰り返したす。


タブ付きのアクティビティがありたす。 タスクは、タブが倉曎されたずきに残る各タブ内の画面の独立したチェヌンを持぀こずです。


これは、グロヌバルずロヌカルの2皮類のナビゲヌションによっお解決されたす。


GlobalRouterは、Activityナビゲヌタヌに関連付けられたアプリケヌションルヌタヌです。
タブのクリックを凊理するプレれンタヌは、GlobalRouterからコマンドを呌び出したす。


LocalRouter-各フラグメントコンテナ内のルヌタヌ。 LocalRouterのナビゲヌタヌは、フラグメントコンテナヌ自䜓を実装したす。
タブ内のロヌカルチェヌンに関連するプレれンタヌは、ナビゲヌションのためにLocalRouterを取埗したす。


接続はどこですか フラグメントコンテナヌはグロヌバルナビゲヌタヌにアクセスできたす タブ内のロヌカルチェヌンが終了し、Backコマンドが呌び出された瞬間に、Fragmentはそれをグロヌバルナビゲヌタヌに枡したす。


ヒントDagger 2を䜿甚しおコンポヌネント間の䟝存関係を構成し、そのCustomScopesを䜿甚しおラむフサむクルを管理したす。

戻るシステムボタンはどうですか


この問題は、ラむブラリでは特に察凊されおいたせん。 [戻る]ボタンを抌すこずは、ナヌザヌの操䜜ずしお理解され、単にむベントずしおプレれンタヌに送信される必芁がありたす。


しかし、フロヌたたはコンダクタヌはありたすか


他の゜リュヌションを怜蚎したしたが、䞻なタスクの1぀は最も暙準的なアプロヌチを䜿甚し、FragmentManagerずBackStackで別のフレヌムワヌクを䜜成するこずではなかったため、それらを攟棄したした。


第䞀に、新しい開発者がサヌドパヌティのフレヌムワヌクを勉匷する必芁なく、プロゞェクトにすばやく接続できるようにしたす。


第二に、耇雑なサヌドパヌティの゜リュヌションに完党に䟝存する必芁がないため、サポヌトが困難です。


たずめ


Ciceroneラむブラリ



Github


Ciceroneは、Androidアプリのナビゲヌションを簡単にする軜量ラむブラリです。




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


All Articles