2016年10月25日火曜日

Fragment に Toolbar を持たせるのはやめなさい

NavigationDrawer や BottomNavigation パターンを実現するために、各画面を Fragment で実装することがあります。 Fragment によって ActionBar に持たせる機能が違うからか、Fragment のレイアウトに Toolbar を持たせて、Fragment で ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); のような処理をさせているコードを見かけることがあります。

やめなさい

(Toolbar をただの View として使って、ActionBar としては使わない(setSupportActionBar()しない)というのであればまだ許容できるが、それならそもそも Toolbar を使う必要がない)

ViewPager のように複数の Fragment を一度に attach する場合、これでは予期しない動作になることがありえます。ちゃんと Fragment に用意されている機能を使ってください。 Fragment で setHasOptionsMenu(true) を呼ぶと onCreateOptionsMenu() が呼ばれるので、Fragment 用の Menu を inflate します。

ViewPager はこの機能を適切に処理しており、現在のページの Fragment の Menu だけ inflate されるようになっています。 また、FragmentTransaction の show() / hide() で Fragment の表示・非表示を切り替える際も OptionsMenu であれば一緒に適切に処理されます。


Fragment にこのような機能があることを知っているにもかかわらず、上記のようなひどいコードを実装してしまう要因として、Menu ではなく View を置きたいという状況があります。
よくあるのが ActionBar に検索用の入力フィールド(EditText)を持たせたい場合です。
Menu ではなく View を置きたいのだから OptionsMenu の機能は使えないと思ってしまうのでしょうか。

OptionsMenu にはこのような用途のために ActionView という機能があります。MenuItem に独自のレイアウト/Viewを設定できる機能です。 Menu リソースの item で android:actionLayout(app:actionLayout)を使ってレイアウトを指定することもできます。
また、android:actionViewClass(app:actionViewClass) で View クラスを指定することもできます。

この ActionView 用に用意されているクラスとして SearchView があります。 SearchView は ActionBar に検索用の入力フィールド(EditText)を持たせてくれるそのものずばりの機能です。文字が入力されているときにクリアボタン(xボタン)が出る機能も実装されています。 これを利用せずにわざわざ自分で実装する意味はあまりないと思いますが、独自でやりたいのであればそれ用のViewクラスを自分で用意して android:actionViewClass で指定すればよいのです。

まとめると、
  • Fragment のレイアウトに toolbar を持たせない
  • Fragment 独自の機能を ActionBar に入れたいときは OptionsMenu の機能を使う
  • OptionsMenu の機能なら ViewPager で適切に処理される
  • OptionsMenu の機能なら FragmentTransaction の show() / hide() で適切に処理される
  • OptionsMenu には独自の View を配置できる ActionView 機能がある


追記1

Toolbar の中に複雑な View を入れること自体をダメだと言っているわけではありません。 Activityのレイアウトで <android.support.v7.widget.Toolbar> に子ビュー持たせるのは別にいいと思います。 このエントリはあくまで Fragment に toolbar を持たせることについての話です。

0 件のコメント:

コメントを投稿