何年にもわたって、Androidナビゲーションパターンのさまざまな実装を見てきました。一部のアプリケーションはアクティビティのみを使用しましたが、他のアクティビティはスニペットやカスタムビューと混合されていました( カスタムビュー )。
私のお気に入りのフラグメントパターンの実装の1つは、「One-Activity-Multiple-Fragments」哲学に基づいています(「 1つのアクティビティ-複数のフラグメント 」)、または単にフラグメントナビゲーションパターン( フラグメントナビゲーションパターン )、アプリケーションの各画面は全画面フラグメントであり、これらのフラグメントのすべてまたはほとんどがアクティビティ内にあります。
このアプローチは、ナビゲーションの実装方法を簡素化するだけでなく、パフォーマンスも向上するため、ユーザーエクスペリエンスが向上します。
この記事では、Androidでの一般的なナビゲーションパターンの実装について説明し、次にスニペットベースのナビゲーションパターンを紹介します。このパターンを実装するサンプルアプリケーションは、にあります。 GitHub 。
アクティビティのみを使用する一般的なAndroidアプリケーションは、ツリータイプの構造(正確には有向グラフ)で構成されており、メインアクティビティは ランチャー 。アプリケーションを閲覧している間、「 バックスタック 」オペレーティングシステムのおかげで維持されている活動の。
簡単な例を次の図に示します。
アクティビティA1はアプリケーションのエントリポイントであり(たとえば、初期画面またはメインメニューを表します)、そこからユーザーはA2またはA3に移動できます。あなたが使用できる活動間で通信する必要があるとき startActivityForResult() または、グローバルにアクセス可能なビジネス論理オブジェクトを両方のアクティビティ間で共有することもできます。
新しいアクティビティを追加する必要がある場合は、次の手順に従う必要があります。
このナビゲーション図はかなり単純化されたアプローチですが、しかし、必要なときに非常に複雑になる可能性があります の操作 バックスタック または、同じアクティビティを数回再利用する必要がある場合。たとえば、ユーザーに異なるチュートリアル画面をナビゲートさせたいが、各画面がベースとして同じアクティビティを使用する場合。
幸いなことに、これらのケースに対応するツールがあります。 雑用 とのためのいくつかのガイド のナビゲーション バックスタック 適切な 。
Scalaを学ぶのは難しい
次に、APIレベル11で、フラグメントが到着しました...
AndroidはAndroid3.0(APIレベル11)にスニペットを導入し、主にタブレットなどの大画面でより動的で柔軟なUIレイアウトをサポートします。タブレットの画面は携帯電話の画面よりもはるかに大きいため、UIコンポーネントを組み合わせるためのスペースが増えます。スニペットは、ビュー階層への複雑な変更を管理する必要なしに、これらのレイアウトをサポートします。アクティビティのレイアウトをチャンクに分割することで、実行時にアクティビティの外観を変更し、それらの変更をアクティビティによって管理されるアクティビティスタックに保持できます。 -Googleガイドからの引用 フラグメントのAPI 。
この新しいおもちゃにより、開発者はマルチパネルのユーザーインターフェイスを作成し、他のアクティビティでコンポーネントを再利用できるようになりました。一部の開発者はこれを気に入っていますが、他の開発者はこれを気に入っています そんなにない 。スニペットを使用するかどうかはよくある議論ですが、スニペットを使用するにはスニペットがさらに複雑になり、開発者がスニペットを正しく理解する必要があることに誰もが同意すると思います。
フラグメントが画面の一部を表すだけでなく、画面全体がアクティビティ内のフラグメントである例をますます多く見始めました。すべてのアクティビティにフルスクリーンスニペットが1つだけあり、他には何もないレイアウトを見たことがありました。これらのアクティビティが存在する唯一の理由は、スニペットを保存することでした。設計上の欠陥とは別に、このアプローチには別の問題があります。下の図を見てください。
A1はどのようにF1と通信できますか?何が起こるかというと、A1はF1を作成したため、F1を完全に制御できます。 A1は、たとえばF1の作成時にパッケージを渡すことも、パブリックメソッドを呼び出すこともできます。 F1はどのようにA1と通信できますか?それはもっと複雑です、それはのパターンで解決することができます コールバック/オブザーバー ここで、A1はF1にサブスクライブし、F1はA1に通知します。
しかし、A1とA2はどのように通信できますか?上で説明したように、彼らは startActivityForResult() 。
そして今、本当の質問:F1とF2はどのように通信できますか?この場合でも、グローバルにアクセス可能で、データの受け渡しに使用できるビジネスロジックコンポーネントを使用できます。しかし、そのコンポーネントは必ずしも派手なデザインと同じではありません。 F2がF1に直接情報を渡す必要がある場合はどうなりますか?そのような場合、パターンで 折り返し電話 F2はA2に通知でき、A2は結果になり、その結果はF1に通知できるA1が保存できます。
このアプローチには多くのコードが必要です ボイラープレート そしてすぐにの源になります バグ 、痛みと怒り。
すべてのアクティビティを取り除き、残りのフラグメントを保持するアクティビティの1つだけを保持できるとしたらどうでしょうか。
優れたデータベース設計には含まれていません
時間が経つにつれて、ほとんどのアプリケーションで「One-Activity-Multiple-Fragments」パターンを使い始めましたが、今でもそれを使用しています。たとえば、このアプローチや哲学については多くの議論があります ここに Y ここに 。私が見逃したのは、自分で見てテストできる特定の例でした。
次の図を少し見てみましょう。
これで、コンテナアクティビティは1つだけになり、複数のフラグメントが再びツリータイプの構造になりました。それらの間のナビゲーションは、によって処理されます FragmentManager 、これは バックスタック 。
phpユニコードからutf8
あなたは今私たちが持っていないことに気付くでしょう startActivityForResult() しかし、パターンを実装することはできます コールバック/オブザーバー 。次に、このアプローチの長所と短所をいくつか見てみましょう。
アクティビティが1つしかないため、新しい画面を追加するたびにマニフェストを更新する必要がなくなりました。アクティビティとは異なり、フラグメントを宣言する必要はありません。
これは小さなことのように思えるかもしれませんが、50を超えるアクティビティを含む大規模なアプリケーションの場合、これにより、 AndroidManifest.xml ファイル。
複数の画面があるサンプルアプリケーションのマニフェストファイルを見てください。マニフェストファイルは非常にシンプルに保たれています。
package='com.exarlabs.android.fragmentnavigationdemo.ui' >
私のサンプルコードでは、私が使用していることに気付くでしょう NavigationManager 私の場合、これは各フラグメントに注入されます。このマネージャーは、 ロギング 、管理 バックスタック とりわけ、ブラウジング動作が他のビジネスロジックから切り離され、異なる画面の実装に分散されないようにします。
ユーザーがユーザーのリストからいくつかのアイテムを選択できる画面を開始したい状況を想像してみましょう。また、年齢、職業、性別など、いくつかのフィルタリング引数を渡したいと思います。
アクティビティの場合、次のように記述します。
Intent intent = new Intent(); intent.putExtra('age', 40); intent.putExtra('occupation', 'developer'); intent.putExtra('gender', 'female'); startActivityForResult(intent, 100);
次に、定義する必要があります onActivityResult どこかで結果を処理します。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); }
このアプローチに関する私の個人的な問題は、これらの引数が「エクストラ」であり、必須ではないことです。したがって、エクストラが欠落している場合は、受信アクティビティがさまざまなケースを処理することを確認する必要があります。後でリファクタリングが行われ、「年齢」などの追加機能が不要になったときに、このアクティビティが開始されたすべてのコードを調べて、すべての追加機能が正しいことを確認する必要があります。
また、結果(人のリスト)がシリアル形式ではなく_リスト_として到着し、その後デシリアル化する必要がある場合は、より良いと思いませんか?
スニペットベースのブラウジングの場合、すべてがより簡単です。あなたがしなければならないのはでメソッドを書くことです NavigationManager と呼ばれる startPersonSelectorFragment() 必要な引数と実装 折り返し電話 。
mNavigationManager.startPersonSelectorFragment(40, 'developer', 'female', new PersonSelectorFragment.OnPersonSelectedListener() { @Override public boolean onPersonsSelected(List selection) { [do something] return false; } });
またはと RetroLambda
mNavigationManager.startPersonSelectorFragment(40, 'developer', 'female', selection -> [do something]);
アクティビティ間で共有できるのは、プリミティブデータまたはシリアル化されたデータを含むパッケージのみです。これでフラグメントを使用してパターンを実装できます 折り返し電話 ここで、たとえば、F1は任意のオブジェクトを渡すF2をリッスンできます。の以前の実装例をご覧ください 折り返し電話 、_List_を返します。
これは、たとえば5つのメニュー項目があるドロワーを使用する場合に明らかになり、各ページにドロワーが再度表示されます。
純粋なアクティビティブラウジングの場合、各ページを膨らませて引き出しを開始する必要がありますが、もちろんこれには費用がかかります。
示されている図では、ルートまたはルートのいくつかのフラグメントを見ることができます ルートフラグメント (FR *)ドロワーから直接アクセスできる画面スニペットであり、これらのスニペットが表示されている場合にのみドロワーにアクセスできます。図のマークされた線の右側にあるものはすべて、任意のナビゲーションスキームの例としてあります。
含まれているアクティビティにはドロワーが含まれているため、ドロワーのインスタンスは1つだけです。したがって、ドロワーが表示される各ナビゲーションステップ もう一度始める必要はありません 。まだすべてがどのように機能するのか確信が持てませんか?ドロワーの使用法が示されている私のサンプルアプリを見てください。
私の大きな恐怖は、プロジェクトでフラグメントナビゲーションパターンを使用すると、ある時点で、フラグメント、サードパーティライブラリ、およびさまざまなバージョンのシステムの複雑さを解決するのが難しい未知の問題に遭遇することです。運用。これまでに行ったすべてのことを屈折させなければならなかった場合はどうなりますか?
実際、あなたは問題を解決しなければならないでしょう ネストされたフラグメント 、次のようなスニペットを使用するサードパーティライブラリ ShinobiControls 、 ViewPagers Y FragmentStatePagerAdapters 。
これらの問題を解決するためにフラグメントの十分な経験を積むことは長いプロセスであったことを認めます。しかし、いずれの場合も、問題は哲学が悪いということではなく、彼が断片を十分に理解していないということでした。しかし、当時の私よりもフラグメントをよく理解していれば、問題はありません。
諮問委員会の形成方法
私が今言及できる唯一の短所は、フラグメントベースのナビゲーションを備えた複雑なアプリケーションのすべての複雑なシナリオを示す成熟したライブラリがないため、解決するのが簡単ではない問題を見つけることができるということです。
この記事では、アプリケーションにナビゲーションを実装するための代替手段を見てきました アンドロイド 。このパターンは、アクティビティを使用する従来のナビゲーション哲学と比較され、従来のアプローチの代わりにフラグメントを使用することが有利であるいくつかの非常に良い理由がわかりました。
まだ確認していない場合は、デプロイ内のデモアプリを確認してください GitHub 。その使用法をよりよく示すことができる良い例を提供することを恐れないでください。
関連: Android開発者が犯す最も一般的な間違いトップ10