AndroidでToolbarを使って見る

# ActionbarからToolbarへ
前回、[ActionBarをFragmentに対応させる](https://qiita.com/cozyk100/items/2e54f9d13426beeedfcb)でActionbarをActivity、と複数フラグメントに対応させましたが、googleは今はActionbarではなくて、Toolbarを推奨しています。

公式ページ：[アプリバーの追加](https://developer.android.com/training/appbar?hl=ja)

公式では、ActionbarやToolbarのように画面の上部に表示されるばーを「アプリバー」と総称しています。

ActionbarとToolbarの大きな違いは
* Actionbarはthemesの一部
* ToolbarはLayoutの部品

が大きな違いです。

## themes.xml
themes.xmlの親スタイルはActionbarの場合は
parent="Theme.MaterialComponents.DayNight.**DarkActionBar**"
でしたが、Toolbarの場合は
parent="Theme.MaterialComponents.DayNight.**NoActionBar**"
もしくは
parent="Theme.Material3.DayNight.**NoActionBar**"
となります。Material3では、DarkActionBarは既になくなっています。Material3ではActionbarはもう使うなってことなんでしょうかね？
```xml:themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.Toolbar" parent="Theme.Material3.DayNight.NoActionBar">
    ・・・
</resources>
```
nightテーマの方のthemes.xmlも同様に修正します。

## メニュー定義ファイル
Toolbarのアイコン、メニューを定義するメニュー定義ファイルはActionbarもToolbarも同じなのでそのまま使います。


## レイアウト、activity_main.xml
Toolbarはレイアウトの中のひとつの部品なのでactivity_main.xmlの上部に配置します。その下にFragmentContainerViewを配置します。Toolbar以外の残りはFragmentContainerViewが目いっぱい、画面全体を覆うようにします。この、FragmentContainerViewをクルクルとFragmentを入れ替えて画面遷移します。

公式ではToolbarの部品に
**android.support.v7.widget.Toolbar**
を使っていますが、これは古いようです。最新版は
**androidx.appcompat.widget.Toolbar**
を使います。

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:elevation="10dp"
        android:minHeight="?attr/actionBarSize"
        android:theme="?attr/actionBarTheme"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:name="com.example.toolbar.Fragment1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/toolbar"
        app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
```
Toolbarの設定値でテーマの設定値を適用する場合は?attrを使います。
* ?attr/actionBarSize バーの縦サイズ
* ?attr/colorPrimary テーマのPrimaryの色
* ?attr/actionBarTheme バーのテーマ

![スクリーンショット 2023-06-27 101009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/2f5b1bdb-44de-c901-fee3-b6bee3b3275b.png)

# MainActivity、Fragmentの作り
MainActivityはActionbarの時と同じように、MainActivity側にToolbarの設定、menuのinflateを集約します。Fragment側は各Fragmentで異なる場合、Toolbarのアイコン、メニューの表示、非表示だけを制御します。
```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {
   /** ViewBinding */
   private lateinit var binding: ActivityMainBinding

    /**
     * アクティビティの初期化
     * @param[savedInstanceState] Bundle
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater).apply {
            setContentView(this.root)
        }
        val toolbar = binding.toolbar.apply {
            setLogo(R.mipmap.ic_launcher)
            title = "タイトル"
            setTitleTextColor(Color.WHITE)
            subtitle = "サブタイトル"
            setSubtitleTextColor(Color.GRAY)
        }
        setSupportActionBar(toolbar)
    }
    ・・・
}
```
レイアウト上に配置してある部品なので普通にviewBindingで取得してプロパティを設定します。サブタイトルは例として設定しいますが。マテリアルデザインでは非推奨となっています。

## メニューのinflateとアクションの追加
公式では[アクションへの対応](https://developer.android.com/training/appbar/actions?hl=ja#handle-actions)で、onOptionsItemSelected関数を実装していますが、この実装は古いようです。

前回、[ActionBarをFragmentに対応させる](https://qiita.com/cozyk100/items/2e54f9d13426beeedfcb)での例と同様に
**androidx.core.view.MenuHost**
**androidx.core.view.MenuProvider**
を使います。これはそっくり、そのままActionbarの時と同じ実装が使えます。
```kotlin:MainActivity.kt
    private fun setupMenuBar() {
        addMenuProvider(object : MenuProvider {
            override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
                menuInflater.inflate(R.menu.menu_sample, menu)
            }
            override fun onMenuItemSelected(item: MenuItem): Boolean {
                when (item.itemId) {
                    R.id.iconItem1 -> {
                        ・・・
                    }
                    ・・・
                    else -> {}
                }
                return true
            }
        })
    }
```

Fragment側も、そのままActionbarの時と同じ実装が使えます。
```kotlin:Fragment1.kt
    private fun setupMenuBar() {
        val menuHost: MenuHost = requireActivity()
        menuHost.addMenuProvider(object : MenuProvider {
            override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
                // Fragmentの場合はinflateしなくていい
            }
            override fun onPrepareMenu(menu: Menu) {
                // Fragment毎に、ActionBarのicon、menuの表示/非表示を制御する場合はここでやる
                menu.findItem(R.id.iconItem1).isVisible = false
                menu.findItem(R.id.iconItem2).isVisible = true
                ・・・
            }
            override fun onMenuItemSelected(item: MenuItem): Boolean {
                // アイコン、メニューが押された場合のハンドリングはMainActivityでやる
                return true
            }
        }, viewLifecycleOwner, Lifecycle.State.RESUMED)
    }
```
Fragment側で実装すべきなのはMenuProvider#onPrepareMenu関数だけです。ここは各Fragment毎に表示するアイコンメニューを変えるため、表示、非表示の制御をします。

# ActionbarからToolbarへの書き換えは意外と簡単
以上のように、ActionbarとToolbarで違うのは
* themes.xml（dayLight、night）の親スタイル
* activitymain.xmlにToolbarを配置
* MainActivity.ktでToolbarの初期化と設定

なので意外と簡単です。

# 最後に
ソースは[gitHub](https://github.com/cozyk100/AndroidStudioProjectsPublic/tree/main/ToolbarKotlin)にpushしました