【Kotlin研修4日目】独自レイアウトのListView、アクションバーへのオプションメニューの実装

# 独自レイアウトのListView
`ListView`の`Item`を独自レイアウトで定義する手順は以下の通り。

> 1. `res/layout`フォルダに独自定義する`レイアウトファイル`を用意
> 2. `Adapter`オブジェクトの生成時、<br>`SimpleAdapter(context:data:resource:from:to:)`の`resource`および`to`に、<br>独自定義した`レイアウトファイル`の`R値`を指定(後述)

## SimpleAdapterオブジェクト生成時のR値

```kotlin:MainActivity.kt
SimpleAdapter(
    context: Context!,
    data: MutableList<out MutableMap<String!, *>!>!,
    resource: Int
    from: Array<String>!
    to: IntArray!
)
// パラメータ
// context: Adapterを生成するアクティビティオブジェクト(=コンテキスト)
// data: Adapterに紐づけるリストデータ
// resource: リスト形式ビュー(ListView)のItemのレイアウトを表現するR値
// from: リストデータ(MutableMap)のキー
// to: リストデータ(MutableMap)の値を埋め込むTextViewのR値(ID)
```

`Android SDK`で用意されたレイアウトを用いる場合の`R値`は`android.R.`、
`独自定義`したレイアウトファイルを用いる場合の`R値`は`R.`から始める。

|パラメータ|`Android SDK`|独自定義|
|:--|:--|:--|
|`resource`|`android.R.layout.`<br>`simple_list_item_2`|`R.layout.`<br>`<レイアウトファイル名>`|
|`to`|`intArrayOf(`<br>`android.R.id.text1,`<br>`android.R.id.text2`<br>`)`|`intArrayOf(`<br>`R.id.<TextViewのid>,`<br>`R.id.<TextViewのid>`<br>`)`|

***

# アクションバー
`アクティビティ`のタイトルや`アクション`を視覚的に表現する、**アプリ上部**に表示されるバー。
![アクションバー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/377648aa-dd0b-f85e-ef5e-22790fd2f460.png)

## オプションメニュー
`アクションバー`内に表示される、`アイコン`形式のメニュー。

## オーバーフローメニュー
`アクションバー`内に十分な余白がなく表示できない`オプションメニュー`が格納される、`文字列`形式のメニュー。

***

# オプションメニューの実装
[参考: メニュー](https://developer.android.com/guide/topics/ui/menus)
`オプションメニュー`を実装する手順は、以下の通り。

> 1. `res`フォルダ内に、`Resource Type: menu`の`Android Resource Directory`を作成
> 2. 1.で作成したフォルダ内に、`Menu Resource File`(`.xml`ファイル)を作成
> 3. `アクティビティ`クラスに、`オプションメニュー`を表示するための`onCreateOptionsMenu()`メソッドを実装
> 4. `アクティビティ`クラスに、オプションメニューの`アイテム`が選択された場合の処理を記述する`onOptionsItemSelected()`メソッドを実装

## Menu Resource Fileの記述
`オプションメニュー`の`Item`含む`レイアウト`を定義する`XML`ファイル。

### Menu Resource File
[参考: メニューリソース](https://developer.android.com/guide/topics/resources/menu-resource?hl=ja)
`アプリ`内に実装する各種`メニュー`のレイアウトを定義する`XML`ファイル。
`メニュー`には、`オプションメニュー`のほか、[`コンテキストメニュー`](https://qiita.com/b150005/items/69fbe3ee34b1253cbaff)、[`サブメニュー`]()が含まれる。

### 定義

```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/<ItemID>"
        app:showAsAction="<アクションバーへの表示設定>"
        android:title="@string/<strings.xmlで定義した文字列の変数名>"
        android:icon="@drawable/<アイコンID>
    />
</menu>
```

### showAsAction

|`Activity`クラスの継承元|属性|
|:--|:--|
|通常の`Activity`|`android:showAsAction`|
|`AppCompatActivity`|`app:showAsAction`|

#### showAsActionの属性値

|属性値|内容|
|:--|:--|
|`never`|`オーバーフローメニュー`に格納|
|`always`|常に`アクションバー`に表示(**非推奨**)|
|`ifRoom`|スペースに余裕があれば`アクションバー`に表示<br>余裕がない場合は`オーバーフローメニュー`に格納|

### icon
`アクションバー`に表示する場合(=`オプションメニュー`)は指定した`アイコン`のみ表示され、
`オーバーフローメニュー`に格納する場合は`title`の文字列のみ表示される。

### サンプルコード

```xml:res/menu/menu_options_menu_list.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/menuListOptionSetMeal"
        app:showAsAction="never"
        android:title="@string/menu_list_options_setMeal"
    />

    <item
        android:id="@+id/menuListOptionCurry"
        app:showAsAction="never"
        android:title="@string/menu_list_options_curry"
    />
</menu>
```

## オプションメニューの表示
`アクティビティ`**開始時**に呼び出される`onCreateOptionsMenu()`メソッドのブロック`{...}`内に、
`オプションメニュー`の`Item`を定義した`XML`を表示する`MenuInflater.inflate()`メソッドを記述する。

### 定義

```kotlin
MenuInflater.inflate(menuRes: Int, menu: Menu!): Unit
// パラメータ
// menuRes: メニューのレイアウトファイル名(R値)
// menu: インフレートを行うメニュー
```

### サンプルコード

```kotlin:MainActivity.kt
class MainActivity: AppCompatActivity() {
    // 定義したオプションメニューの表示
    // onCreateOptionsMenu(menu:): アクティビティ開始時に呼び出されるメソッド
    // menu: Itemを保持するオプションメニュー
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        // レイアウトファイルのインフレート
        menuInflater.inflate(R.menu.<XMLファイル名>, menu)
        // onCreateOptionsMenu()のオーバーライド時は、常にtrueを返却
        return true
    }
}
```

### インフレート(inflate)
`.xml`ファイルに記述した`ビュー`を`Javaオブジェクト`として`実体化`すること。

## オプションメニューのアイテム選択時の処理
オプションメニューの`アイテム`は、既定で`リスナ`として定義されているため、
`アイテム`**選択時**に呼び出される`onOptionsItemSelected()`メソッドを用いて、
選択された`アイテム`に応じた分岐処理(`イベントハンドラ`)を実装する。
※`アイテム`が選択されない場合は、`super.onOptionsItemSelected()`の返り値(=`false`)を返却する必要がある。

### サンプルコード

```kotlin:MainActivity.kt
class MainActivity: AppCompatActivity() {
    // オプションメニューItemの"タップ"イベント検知時の処理
    // onOptionsItemSelected(item:): オプションメニューのItemがタップされた場合に呼び出されるメソッド
    // item: タップされたItem
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // 最終的に返却するBool値(初期値: true)
        var returnVal: Boolean = true

        // タップされたItemに応じた分岐処理
        // MenuItem.itemId: ItemのID(R値)
        when(item.itemId) {
            R.id.<レイアウトファイルで定義したID> ->
                // Itemタップ時の処理
                ...

            R.id.<レイアウトファイルで定義したID> ->
                // Itemタップ時の処理
                ...

            else ->
                // オプションメニューのItem以外が選択された場合は、
                // 親クラス(super)のonOptionsItemSelected(item:)メソッドの
                // 返り値(デフォルトではfalse)を返却
                returnVal = super.onOptionsItemSelected(item)
        }
        ...
        // 最終的にBool値を返却
        // -> アイテムが選択され、正常に処理を行った場合はtrueを返却
        return returnVal
    }
}
```

## アクションバーへの「戻る」ボタン(←)の実装
[参考: ActionBar](https://developer.android.com/reference/kotlin/android/app/ActionBar)
`アクションバー`を表す`Nullable`型の`supportActionBar`プロパティの、`setDisplayHomeAsUpEnabled()`メソッドを、引数を`true`にして呼び出す。
なお、「戻る」ボタンの`R値`は、`android.R.id.home`で表される。

### 定義

```kotlin
ActionBar.setDisplayHomeAsUpEnabled(showHomeAsUp: Boolean): Unit
// パラメータ
// showHomeAsUp: 1つ階層が上のアクティビティに戻る「←」ボタンの表示有無
// true: 表示
// false: 非表示
```

### サンプルコード

```kotlin:MenuThanksActivity.kt
class MenuThanksActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // アクションバー内に「戻る」ボタン(←)を表示
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        var returnVal: Boolean = true

        if (item.itemId == android.R.id.home) {
            finish()
        }
        else {
            returnVal = super.onOptionsItemSelected(item)
        }

        return returnVal
}
```