特定の Fragment でだけ使うメニュー項目を共用の Toolbar に inflate する


概要

Activity が持つ Toolbar の Overflow メニューに、特定の Fragment だけで使うメニュー項目を挿入する方法を考えてみました。

背景

Toolbar を持つ Activity で、 Fragment A と Fragment B を入れ替えます。Activity で Toolbar に inflate したメニュー項目は Fragment A/B 両方で表示されるので、Fragment A でしか必要のないメニューは Fragment A を表示しているときだけ出したい、と思いました。

実行環境

項目
Android OS 4.3
compileSdkVersion 25
buildToolsVersion "25.0.3"

よりよい実装方法はこちら


実装

onCreateView で menu を inflate し、onDetach で挿入した menu を削除するというやり方でやってみました。menu を挿入・削除する対象の Toolbar は getActivity() して取得できる Activity オブジェクトから findViewById して取得しています。


import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.graphics.ColorUtils;
import android.support.v7.widget.Toolbar;

public class FragmentA extends Fragment {

    //...

    @Nullable
    @Override
    public View onCreateView(
            final LayoutInflater inflater,
            @Nullable final ViewGroup container,
            @Nullable final Bundle savedInstanceState
    ) {
        //...
        inflateMenuToToolbar();
    }

    private void inflateMenuToToolbar() {
        final FragmentActivity activity = getActivity();
        final Toolbar toolbar = (Toolbar) activity.findViewById(R.id.toolbar);
        toolbar.inflateMenu(R.menu.fragment_a_menu);
        // チェックするタイプのメニューだったらチェックを入れる.
        // toolbar.getMenu().findItem(R.id.menu_checkable).setChecked(checked);
        toolbar.setOnMenuItemClickListener(item -> {
            if (item.getItemId() == R.id.menu_checkable) {
                // メニューがタップされた時の処理をここに記述.
                return true;
            }
            // Activity が持つメニューを呼び出す.
            return activity.onOptionsItemSelected(item);
        });
    }

    //...

    @Override
    public void onDetach() {
        super.onDetach();
        final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
        toolbar.getMenu().removeItem(R.id.menu_checkable);
    }

微妙な点

  1. Activity の Toolbar の ID を持つ(あるいは Activity クラスの public 定数として定義する)ようにしないといけない
  2. Activity が Toolbar を持っているかどうか、null チェックするくらいしか確認する方法がない
  3. 挿入した menu を逐一 onDetach で削除しないといけない

おわりに

やってみたところ、実装はできましたが、あまり良いやり方に感じませんでした。よりよい方法をご存知でしたらお教えくださいますと幸いです。


参考

下記ブログの方法を参考に致しました。
- AndroidのFragmentでActivityのToolbarを利用する


追記

上で私が微妙だと書いた点がすべて解消されている最適なやり方を、roana0229 さんからコメントで教えていただいたので、そちらの方法で修正しました。

1. Toolbar を持つ Activity で setSupportActionBar する

Activity
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        // ...
        setSupportActionBar(binding.appBarMain.toolbar);
    }

2. Fragment 固有のメニューを inflate する

onCreateView で setHasOptionsMenu(true); を呼び、 onCreateOptionsMenu で inflate します。

Fragment
    @Nullable
    @Override
    public View onCreateView(
            final LayoutInflater inflater,
            @Nullable final ViewGroup container,
            @Nullable final Bundle savedInstanceState
    ) {
        // ...
        setHasOptionsMenu(true);
    }

    @Override
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.search_menu, menu);

        final MenuItem item = menu.findItem(R.id.menu_checkable);
        // チェックするタイプのメニューだったらチェックを入れる.
        // item.setChecked(checked);
        item.setOnMenuItemClickListener(i -> {
            // メニューがタップされた時の処理をここに記述.
            return true;
         });
    }