Honeycomb から登場した Fragment によって、すべてのコントローラロジックが Activity に集約されてしまう呪縛から解き放たれ、人類は平和を取り戻しました。Fragment はまた、Support Library のメンバとして、Gingerbread 以前の過去世に積み上げた罪業をも消し去ろうとしています。
そんな Fragment ですが、いまいち使い方が分からない、という声も時折耳にします。本稿では、Android アプリケーションの開発を始めて Activity と Fragment について学び、その両者の住み分けに悩んでいる方向けに、効果的な Fragment の作り方を紹介しようと思います。
Fragment がなかった暗黒の時代
Fragment がなかった時代、人類はどのようにして Android アプリケーションを作っていたのでしょうか。
タブを使った画面の切替
複数のタブを持ち、タブごとに異なるコンテンツを表示するようなアプリケーションを作ると仮定します。
このようなアプリケーションを作るには、以下の手順でタブの機能とコンテンツの表示を実現します。
- タブ切り替えをハンドリングするホストとなる Activity を作る
- タブの数だけ、コンテンツを表示する Activity を作る
ざっくりとした手順では数こそ多くないものの、この方法にはいくつかの問題が潜んでいます。
- Activity という割と大きなオブジェクトを、タブのホストとタブのコンテンツの数だけメモリに乗せないといけない
- いくつかの専用のコンポーネントとガッツリ連携しなければ実現できないため、柔軟性に乏しい
レイアウトの使い回し
Android のレイアウトの仕組み上、xml に以下のようなコードを仕込んでおくと、レイアウトリソースの使い回しが実現できます。
<include layout="@layout/hogehoge" android:id="@+id/included_layout">
これは非常に便利な反面、View のレイヤでしか使い回しが効かないので、それを統括するコントローラのロジックまでは使い回しが出来ません。
ダイアログの生成とライフサイクル
ダイアログは、Activity が管理します。Activity にはライフサイクルが有り、ライフサイクルが終わるとその役目も終えなければならず、例えば、画面回転をすると、表示していたダイアログも消えてしまいました。
そのため、ダイアログの表示に関するフラグを持ち越すコードを記述し、そのフラグを見て、新しい Activity がダイアログの表示を継続するかどうか判断しなければなりませんでした。
Fragment の登場による文明開花
Honeycomb になり、Fragment は ActionBar と共に颯爽と現れました。
そして、タブ機能を実現するための既存のコンポーネント群は @Deprecated となり、代わりに、Fragment の使用を促されるようになりました。
ActionBar により、アプリ内のナビゲーションに関する振る舞いが統一され、それにともなって、ActionBar との組み合わせとして手軽に扱うことの出来る Fragment の使用が推奨されるようになったのです。
Fragment とは
Android Developers の Fragment の Javadoc には、以下のようにあります。
A Fragment is a piece of an application's user interface or behavior that can be placed in an Activity.
UI を持ち、その UI の振る舞いを管理する、Activity の中に組み込むことが出来るコンポーネントが Fragment です。つまり、これによって、<include>
タグだけでは実現できなかった、コントローラのロジックを使いまわす事ができるようになったのです。
また、ActionBar にかぎらず、ViewPager のような View コンポーネントからも Fragment が取り扱えます。以前のタブと異なり、実装に用いるインタフェースも単純化されたため、タブほどの実装コストも掛けずに同等の機能を実装することが出来るようになりました。
Dialog に関しては、専用の DialogFragment が提供され、Fragment の管理ポリシーに基いて DialogFragment も管理されるようになったため、以前のように、表示に関するフラグを持ったり、そのフラグを持ち越したりと言ったコードを自分で実装する必要がなくなりました。
マルチデバイス時代の到来
今や Android は、スマートフォンのようなハンドセット端末にとどまらず、タブレット端末でも使われます。そして、iOS のユニバーサルアプリケーションに見られるように、ハンドセット端末とタブレット端末で共通のアプリを配信する仕組みが整備され、1 つのアプリケーションが様々な端末へと配信できるようになりました。
画面の大きさの違いに対応する
ハンドセット端末とタブレット端末の違いで最も大きな違いは、画面の大きさです。タブレット端末の方が、より広い場所を使うことが出来るようになります。
この時、ハンドセット端末向けに作られたレイアウトをそのまま表示してしまうと、妙に伽藍とした画面になってしまうことでしょう。これでは、折角の大きく広い画面が無駄になってしまいます。
例えば、あるコンテンツの一覧画面と、各項目の詳細画面があるとします。
ハンドセット端末では、画面が小さいので、それぞれの画面は別の Activity として作るほうがよいでしょう。しかし、タブレット端末では、一覧表示だけ、詳細表示だけでは、かなりのスペースを無駄遣いしてしまいます。そこで、タブレット端末では、一覧表示を左端に寄せ、一覧から項目を選択すると、その左側に詳細表示を行うようにすします。そうすることで、広い画面を有効に活用できます。
このようなとき、一覧表示や詳細表示が、Fragment によってハンドリングされていたら、ハンドセット端末とタブレット端末への対応が、ぐっと楽になります。
なぜなら、ハンドセット端末向けには、一覧用と詳細表示用のそれぞれの Activity を作り、その Activity は Fragment を配置するロジックさえ持っておけば良く、また、タブレット端末向けには、1 つの Activity が、一覧用と詳細用の Fragment を配置するロジックを持っておけば済むようになるからです。
…こうして世界は Fragment に包まれた…
この他にも、ViewPager と Fragment はとても相性良く連携します。ActionBar にも、簡単なインタフェースで実装できるタブの機能があり、ViewPager と ActionBar のタブの機能は、単純なインタフェースで連携することが出来ます。
実は、Fragment は UI を持たない実装をすることもできます。このようにすることのメリットは、Fragment にライフサイクルがあり、それが Activity のものと緊密に連動することから、Activity のためのデータを、ライフサイクルに合わせて管理するホルダーのような役割を持つことができることに有ります。
Activity のライフサイクルに合うようにクラスを設計するのは、面倒がつきまといます。それを、Fragment が肩代わりしてくれるのです。
また、役割の多い Activity ほど、より多くのデータを取り扱います。メンバ変数としてデータを保持するのではなく、データの保持を Fragment に任せることで、Activity の見通しが良くなります。
(若干の誇張表現がある気がするけどまあいいや…)