Androidアプリ開発 Toolbar 上部のアイコン式のメニュー おまけでKotlin有り

今回はアプリでよく見る上部のバーにアイコン式で設置されてるメニューを実現する**Toolbar**を使う。

Toolbarを上部メニューとして使う上で、**準備しなきゃいけない事がいくつかある**。
・**レイアウト**: Toolbarを置いただけ。

```xml:toolbar_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>
```
・**Activity**:

```java:MainActivity.java
public class MainActivity extends AppCompatActivity{

    Toolbar toolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.toolbar_layout);
        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.add:
                
                break;
        }
        return false;
    }
}
```
・**Toolbar**にセットする情報は**メニューに使うリソースを参照する**為、**res階層menuフォルダ**に**menu用のリソースファイル**を作る。
(menuフォルダを右クリック→New→Menu resource file

```xml:main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/add"
        android:icon="@mipmap/baseline_add_black_48"
        android:title="@string/add"
        app:showAsAction="ifRoom" />
</menu>
```
・**icon**: 項目用のアイコン
・**title**: タイトル
・**app:showAsAction**: 仮に上部のメニューバーにアイコンを表示する十分な領域がなかったらどうするか。一つだけだったら特に気にしなくていい。

項目を増やすには<itemで追加はできるが、追加しすぎると分かりやすさが犠牲になったり、表示領域が足りなくなったりするので、**多くても2つか3つがGoogleの推奨数**。

アイコンについては、**自ら追加しない限りデフォルトではアイコン用の画像しか用意されてない**ので、手動で追加する必要がある。ただ、Androidは端末によって画面サイズや解像度がバラバラなので、できるだけ最適な素材を用意しないと端末によっては見え方が汚くなる。
よく使うアイコンとかはGoogleがアプリとかで使える用の画像を用意してくれてるサイトがあるので、そこから取るのがいいだろう。(ただし、**ルールは破らないように。**
https://material.io/tools/icons/?style=baseline

上からAndroid向けの物をダウンロードすると、こんな感じで展開される
<img width="882" alt="add_content.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/245077/81241fa3-eb7f-e92a-8e8a-edee24972497.jpeg">
それぞれのサイズに合った素材が用意されてるので、各フォルダから使う画像をコピペする
<img width="882" alt="image_sizes.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/245077/db05d9d6-c3e7-2652-892f-d041322182ea.jpeg">
コピペ先はAndroid Studioの**res階層のmipmapフォルダ**で、使うサイズを決めて**hdpiからxxxhdpi**に追加する。(今回は48

あとはAndroid Manifest.xmlを編集する必要がある。

```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="galaxysoftware.sampleapp">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".KotlinActivity">
        </activity>
    </application>

</manifest>
```
ここの**android:theme=**の箇所を**NoActionBarと、ActionBarがないテーマに変更する必要がある**。(後々ToolbarにActionBarとしての役割を持たせる際に障害になる


準備は整ったので、Activity側の実装を見ていく。

まず、Toolbar用の変数を用意した後、安定の
toolbarにfindVIewByIdでViewを参照付けて**setSupportActionBar()**を実行。これによって**ToolbarをActionBarと同じ様に**使う事ができる。
・**onCreateMenu()**: Activity開始時にメニューを設定する為に呼ばれる
**getMenuInflater().inflate(R.menu.main, menu);**
これで設置したいメニューを指定する。

・**onOptionsItemSelected()**: 設置したメニューの項目がクリックされた時に呼ばれる。
押された項目の判別は引数のmenuからitemidを取得して、メニューファイルで設定したidを指定する。

ここまでがToolbarにメニューを設置して上メニューにする実装だが、設置しただけの最低限の事しかしてないから、レイアウトや押した時の反応はすっからかん。

#もし画面遷移した時とか、一度設定したメニューを別の物に変えたくなった時
Activityに用意されている**invalidateOptionsMenu()**を実行する事で
・**onPrepareOptionsMenu()**: が呼ばれ、ここでメニューを指定する事で変えられる

#Toolbarに検索バーを設置する: SearchView

```xml:main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/search"
        android:icon="@mipmap/baseline_search_black_48"
        android:title="@string/search"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always" />

    <item
        android:id="@+id/add"
        android:icon="@mipmap/baseline_add_black_48"
        android:title="@string/add"
        app:showAsAction="ifRoom" />
</menu>
```
<itemに**app:actionViewClass="android.support.v7.widget.SearchView"**を追加する事で**SearchView**としての機能を持たせる事ができる。

Toolbarに検索バーを設置するには**SearchView**と呼ばれる物を使う。これはメニューの一種なので、定義するのはレイアウトファイルではなく、メニューファイルになる。

Activityから操作するコードは以下の通り。
このSearchViewはmenuに定義してあるので、**通常のfindViewById()ではなく、menu内から探す様にする**。で**menu.findItemが直接SearchViewの型を返す訳ではない**ので、**キャストしてSearchViewとして扱う様にする**。
また、**SearchViewが含まれるメニューを設置する時じゃないとクラッシュする**ので気を付けよう。

```java:MainActivity.java
SearchView searchView = (SearchView)menu.findItem(R.id.search).getActionView();
searchView.setIconified(false);
searchView.setQueryHint(getString(R.string.search));
searchView.clearFocus();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
   @Override
   public boolean onQueryTextSubmit(String s) {
       return false;
   }
   @Override
   public boolean onQueryTextChange(String s) {
       return false;
   }
});
```
・**setIconified()**: 検索ボックスを出さずにアイコン化して表示するか
・**setQueryHint()**: 「検索」の様に、ヒントを表示する
・**clearFocus()**: フォーカスを外す。キーボードが開いてたら閉じる。
・**setOnQueryTextListener()**: SearchViewの文字入力を監視するリスナー
・**onQueryTextSubmit()**: 検索ボタンを押した時に呼ばれる。引数はSearchViewに入力されてる文字
・**onQueryTextChange()**: SearchViewに入力されてる文字が変わる度に呼ばれる。引数は入力されてる文字。もし、suggestionとか予測を出す場合はここを活用することになる。

Toolbarにメニューを実装してよくある上部のアイコン式メニューを実装するやり方は以上となる。実行するとこんな感じ。でも最低限しか実装してないから何も表示されない。
<img width="431" alt="result.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/245077/4bfe0d17-5f5e-a8d2-a252-85f6e5ef83f0.jpeg">
#おまけ：これをKotlinで書くとどうなるか

```kotlin:KotlinActivity.kt
class KotlinActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.toolbar_layout)
        setSupportActionBar(toolbar)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.main, menu)
        val searchView = menu?.findItem(R.id.search)?.actionView as SearchView
        searchView.apply {
            isIconified = false
            queryHint = getString(R.string.search)
            clearFocus()
            setOnQueryTextListener(object : SearchView.OnQueryTextListener{
                override fun onQueryTextChange(query: String?): Boolean {
                    return false
                }

                override fun onQueryTextSubmit(query: String?): Boolean {
                    return false
                }
            })
        }
        return super.onCreateOptionsMenu(menu)
    }

    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when(item?.itemId) {
            R.id.add -> SampleFragment.newInstance()
        }
        return super.onOptionsItemSelected(item)
    }
}
```
感が良い人は気付いたかもしれないが、メニューをクリックした時にadd項目だったら**SampleFragment(Fragment)**を生成しているのだ！

後々やるのだが、この**Fragment**という物が**Androidでの画面遷移**において重要な役割を果たすことになる。