アプリバーを使用する

トップ アプリバーは、アプリの上部に沿って常に同じ場所に配置され、現在の画面からの情報とアクションを表示します。

トップ アプリバーの例
図 1. トップ アプリバーの例

アプリバーの所有者はアプリのニーズによって異なります。フラグメントを使用する場合、ホスト アクティビティ、またはフラグメントのレイアウト内のツールバーが所有する ActionBar として、アプリバーを実装できます。

すべての画面で、常に上部に配置され画面の幅に広がる同じアプリバーを使用する場合は、アクティビティがホストするテーマ アクションバーを使用する必要があります。テーマによって提供されるアプリバーを使用すると、一貫した外観を維持し、オプション メニューとアップボタンをホストする場所を提供できます。

複数の画面でアプリバーのサイズ、配置、アニメーションをきめ細かく制御する場合は、フラグメントがホストするツールバーを使用します。たとえば、折りたたみ式のアプリバーや、画面の半分の幅だけに広がり垂直方向の中央に配置されるアプリバーが必要な場合があります。

メニューをインフレートしたり、ユーザーの操作に応答したり、状況によって異なるアプローチが必要になります。さまざまなアプローチを理解し、最適なアプローチを採用することで、時間を節約し、アプリを適切に機能させることができます。

このトピックの例では、編集可能なプロフィールを含む ExampleFragment を参照しています。フラグメントは、アプリバーで次の XML 定義のメニューをインフレートしています。

<!-- sample_menu.xml -->
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

メニューには 2 つのオプションがあります。1 つはプロフィール画面に移動するオプションで、もう 1 つはプロフィールに加えられた変更を保存するオプションです。

アクティビティが所有するアプリバー

アプリバーは、一般的にはホスト アクティビティによって所有されます。アプリバーがアクティビティによって所有されている場合、フラグメントは、フラグメントの作成時に呼び出されるフレームワーク メソッドをオーバーライドすることにより、アプリバーとやり取りできます。

アクティビティに登録する

アプリバーのフラグメントがオプション メニューの集団に参加していることをシステムに通知する必要があります。そのためには、フラグメントの onCreate(Bundle) メソッドで setHasOptionsMenu(true) を呼び出します。次の例をご覧ください。

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

public class ExampleFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

setHasOptionsMenu(true) は、フラグメントがメニュー関連のコールバックを受け取ることをシステムに伝えます。クリックなどのメニュー関連のイベントが発生すると、まずアクティビティでイベント処理メソッドが呼び出され、その後、フラグメントで呼び出されます。

ただし、アプリロジックでこの順序に依存しないでください。同じアクティビティが複数のフラグメントをホストする場合、各フラグメントがメニュー オプションを提供できます。その場合、コールバックの順序は、フラグメントが追加される順序によって異なります。

メニューをインフレートする

メニューをアプリバーのオプション メニューに統合するには、フラグメント内で onCreateOptionsMenu() をオーバーライドします。このメソッドは、現在のアプリバー メニューと MenuInflater をパラメータとして受け取ります。メニュー インフレータを使用してフラグメントのメニューのインスタンスを作成し、それを現在のメニューに統合します。次の例をご覧ください。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

図 2 は、更新されたメニューを示しています。

メニュー フラグメントが追加されたオプション メニュー
図 2. メニュー フラグメントが追加されたオプション メニュー

クリック イベントを処理する

オプション メニューに参加しているすべてのアクティビティとフラグメントは、タップ操作に応答できます。フラグメントの onOptionsItemSelected() は、選択されたメニュー項目をパラメータとして受け取り、タップが消費されるかどうかを示すブール値を返します。アクティビティまたはフラグメントが onOptionsItemSelected() から true を返すと、他の参加フラグメントはコールバックを受信しなくなります。

onOptionsItemSelected() の実装で、メニュー項目の itemId に対して switch ステートメントを使用します。選択されたアイテムが自分のものであれば、タップを適切に処理して、クリック イベントが処理されることを示す true を返します。選択したアイテムが自分のものでなければ、super の実装を呼び出します。デフォルトでは、super の実装によって false が返され、メニュー処理が続行されます。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // Navigate to settings screen.
                true
            }
            R.id.action_done -> {
                // Save profile changes.
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // Navigate to settings screen.
                return true;
            }
            case R.id.action_done: {
                // Save profile changes.
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

メニューを動的に変更する

ボタンの非表示 / 表示やアイコンの変更を行うロジックは、onPrepareOptionsMenu() に配置します。このメソッドは、メニューが表示される直前に呼び出されます。

前の例で言うと、[保存] ボタンはユーザーが編集を開始するまでは表示されず、ユーザーが保存したときに非表示になるべきです。このロジックを onPrepareOptionsMenu() に追加すると、メニューが適切に表示されます。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

メニューを更新する必要がある場合(たとえば、ユーザーがプロフィール情報を編集するために [編集] ボタンを押した場合)は、ホスト アクティビティで invalidateOptionsMenu() を呼び出して、システムが onCreateOptionsMenu() を呼び出すようにリクエストします。無効化が完了したら、onCreateOptionsMenu() で更新を実行できます。メニューがインフレートされると、システムはフラグメントの現在の状態を反映させるために onPrepareOptionsMenu() を呼び出してメニューを更新します。

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

フラグメントが所有するアプリバー

アプリのほとんどの画面でアプリバーが不要な場合や、ある画面で他とは異なるアプリバーが必要な場合は、フラグメント レイアウトに Toolbar を追加できます。Toolbar はフラグメントのビュー階層内のどこにでも追加できますが、通常は画面の最上部に配置します。フラグメント内で Toolbar を使用するには、他のビューの場合と同様に、ID を指定してフラグメント内で ID への参照を取得します。また、CoordinatorLayout の動作を使用して、ツールバーをアニメーション化することもできます。

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

フラグメント所有のアプリバーを使用する場合は、Toolbar API を直接使用することをおすすめします。setSupportActionBar()Fragment メニュー API は使用しないでください。それらはアクティビティが所有するアプリバーにのみ適しています。

メニューをインフレートする

Toolbar のコンビニエンス メソッド inflateMenu(int) は、メニュー リソースの ID をパラメータとして受け取ります。XML メニュー リソースをツールバーにインフレートするには、このメソッドに resId を渡します。次の例をご覧ください。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

別の XML メニュー リソースをインフレートするには、新しいメニューの resId でこのメソッドを再度呼び出します。新しいメニュー項目がメニューに追加されます。既存のメニュー項目は変更も削除もされません。

既存のメニューセットを置き換える場合は、新しいメニュー ID で inflateMenu(int) を呼び出す前に、メニューをクリアします。次の例をご覧ください。

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

クリック イベントを処理する

setOnMenuItemClickListener() メソッドを使用して、OnMenuItemClickListener をツールバーに直接渡すことができます。このリスナーは、ユーザーがツールバーの端にあるアクション ボタンまたは関連するオーバーフローからメニュー項目を選択する際に呼び出されます。選択された MenuItem がリスナーの onMenuItemClick() メソッドに渡されます。これをアクションを消費するために使用できます。次の例をご覧ください。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen.
                    true
                }
                R.id.action_done -> {
                    // Save profile changes.
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen.
                    return true;
                case R.id.action_done:
                    // Save profile changes.
                    return true;
                default:
                    return false;
            }
        });
    }
}

メニューを動的に変更する

フラグメントがアプリバーを所有している場合、他のビューとまったく同じように、実行時に Toolbar を変更できます。

前の例で言うと、[保存] メニュー オプションはユーザーが編集を開始するまでは表示されず、ユーザーにタップされたときに再度非表示になるべきです。

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

ナビゲーション ボタンが存在する場合は、ツールバーの先頭に表示されます。ツールバーにナビゲーション アイコンを設定すると、そのアイコンは表示されるようになります。ユーザーがナビゲーション ボタンをクリックするたびに呼び出されるナビゲーション固有の onClickListener() も設定できます。次の例をご覧ください。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere.
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere.
            }
        });
    }
}