BottomNavigationView入門

## はじめに
[FiNC](https://finc.com/)という会社でAndroidのアプリ開発を行っているんですが、
ドロワーにある機能に対してユーザーの遷移率が芳しくありませんでした。
僕自身Android歴が長いので違和感を感じませんでした。

よくよく考えてみると、かの有名なSNSアプリなどを利用する際にも、
自分はドロワーを利用する頻度はほとんどないなということに気づきました。
現状のドロワーの実装を進展させるために新しくリリースされた、
BottomNavigationViewを実装してみました。

## BottomNavigationViewとは

![image](https://qiita-image-store.s3.amazonaws.com/0/43762/4a1684a2-5fc1-caa7-e3df-999fd7c17a97.png)

これです。
この役割ってなんなの？というところなんですが、公式ドキュメント曰く、
`Bottom navigation bars make it easy to explore and switch between top-level views in a single tap.`
[公式ドキュメント](https://material.google.com/components/bottom-navigation.html)
ワンタップで、トップレベルのビューの行き来できる魔法のようなものらしいです。
iOSとかでいうグローバルナビゲーションですね。(今更感が否めないですが)


自分の記憶が正しければ、マテリアルデザインガイドラインには、2016年の5, 6月？くらいには記載されていたと思うのですが、実際にリリースされるプロダクトのAPIとして提供されておりませんでした。

そのAPIが先日リリースされました。
[support library revision history](https://developer.android.com/topic/libraries/support-library/revisions.html)

## BottomNavigationViewの構成クラス群
![スクリーンショット 2016-10-27 9.45.24.png](https://qiita-image-store.s3.amazonaws.com/0/43762/5090a778-fa53-45c4-dfb3-c8c669afbde2.png "スクリーンショット 2016-10-27 9.45.24.png")

* a.s.d.widget.BottomNavigationView (FrameLayout)
* a.s.d.<font color="red">internal</font>.BottomNavigationPresenter
* a.s.d.<font color="red">internal</font>.BottomNavigationMenuView (ViewGroup)
* a.s.d.<font color="red">internal</font>.BottomNavigationItemView (FrameLayout)


BottomNavigationViewを構成するクラスは主に上記なんですが、実際に利用する一番上のクラス以外はinternalで直接扱うことは恐らくないです。
このクラス群はMVPの構造になっているようです。(後ほど記載)


## 具体的な実装ステップ
ちゃっかり実装自体は、とても簡単です。

* Step1: menu.xmlを作成する
* Step2: layout.xmlを作成する
* Step3: listenerをセットする。

### ・Steps1


build.gradleでsupport libraryのバージョンを最新に

```build.gradle
dependencies {
    // これでBottomNavigationViewが利用可能
    compile 'com.android.support:appcompat-v7:25.0.0'
    
    // 他のdesign support libsも使いたいので以下を追加
    compile 'com.android.support:design:25.0.0'
}
```

res/menu内にxmlファイルを追加。
よくあるOptionMenuの実装と同じようにする。

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

    <item
        android:id="@+id/item1"
        android:icon="@android:drawable/ic_btn_speak_now"
        android:title="item1" />

    <item
        android:id="@+id/item2"
        android:icon="@android:drawable/ic_delete"
        android:title="item2" />

    <item
        android:id="@+id/item3"
        android:icon="@android:drawable/ic_dialog_alert"
        android:title="item3" />

</menu>
```

### ・Step2
レイアウトのxmlを実装する。
Step1で作成した、menuのxmlをappパッケージのネームスペースのmenuにセットする。
(※公式はdesignパッケージ以下のネームスペースらしいですが、上手く動かないそうです。2016/10/26現在)
ここはCoordinatorでなくてもいいです。BottomNavigationViewはFrameLayoutのサブクラスです。

```activity_main.xml
<!-- xml -->
<a.s.d.w.CoordinatorLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <a.s.d.w.BottomNavigationView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom" 
        app:menu="@menu/item_bottom_navigation" />

</a.s.d.w.CoordinatorLayout>
```

BottomNavigationViewにはセットできる属性がいくつかあります。


|属性|Javaメソッド|内容|
|:--|:--|:---|
|itemBackground|setItemBackgroundResource|BottomNavigationViewの背景色全体を変更する。|
|itemIconTint|setItemIconTintList|itemのアイコンカラーを変更する。|
|itemTextColor|setItemTextColor|itemのテキストカラーを変更する。|


### ・Step3
BottomNavigationViewにアイテムを選択した際のリスナーをセットする。
選択時の処理は自分が表示したいFragmentをセットします。

アニメーションに関してですが、マテリアルガイドラインによると、
BottomNavigationViewを利用している各ページにおいて、
選択状態のページを再度押すとそのページの一番TOPにスクロールするそうです。
(なので、switchはitem.isChecked()とかで切り分けするのがいいのかも。)

```MainActivity.java
bnv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.item1:
                setFragment();
                return true;
            case R.id.item2:
                setFragment();
                return true;
            case R.id.item3:
                setFragment();
                return true;
            }
        return false;
    }
});
```

## その他
### アイテム数の上限
* Itemをmenu.xmlに6個以上追加すると、以下のIllegalArgumentExceptionが起こります。ビジネスの要望などで、6個以上追加しようとするので、エンジニアを救うための救済策でしょうか？Googleナイス。

> Maximum number of items supported by BottomNavigationView is 5. 
Limit can be checked with BottomNavigationView#getMaxItemCount()

### Custom Behaviorが必要
* BottomNavigationViewでは、スクロール to Bottomした際にスクリーンアウトする必要があります。現状のCoordinatorLayoutのbehaviorのプロパティではこの動きを実現できません。ですので、独自でカスタムBehaviorクラスを実装して、xmlでその部分を指定します。(完成したらアップします。以下の内部を実装する予定。)

```java
public class BisonBehavior extends CoordinatorLayout.Behavior<BottomNavigationView> {

    public BisonBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, BottomNavigationView child, View dependency) {
        return dependency instanceof FrameLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, BottomNavigationView child, View dependency) {
        return true;
    }

}
```

### xmlの組み方をどうするか？
自分はこんな感じで組んだんですが、まだActivityとFragmentで上手く切り分けられていない気がしています。

```xml
<CoordinatorLayout>
    <AppBarLayout>
        <Toolbar /> 
    </AppBarLayout>
    <FrameLayout />　<!-- ここにページごとのFragmentをセットする。 -->
    <BottomNavigationView />  
</CoordinatorLayout>
```


## おまけ


![Kobito.H9YAS9.png](https://qiita-image-store.s3.amazonaws.com/0/43762/106f8255-c337-ba88-c109-38a35ba721bd.png "Kobito.H9YAS9.png")

BottomNavigationViewはMVPで実装されています。
BottomNavigationViewは内部にPresenterを保持していて、そのPresenterがMenuとMenuItemをmenu.xmlからパース(モデル化)して、各viewに繋ぎこんでいます。

## 所感
BottomNavigationViewというくらいなので、Viewとしての役割を意識させたかったのかなと思っています。
なので、Fragmentに入れちゃうのもいいのかななんて考えています。
はじめはガイドライン読んでなかったので、
BottomNavigationView + ViewPagerのようにSwipeでページングするものとの実装を行っていました。
(これはダメな実装です。)
BottomNavigationViewで移動できるのはアイテムをクリックした時のみです。
少し触っただけなので、実際に実装する際のベストプラクティスはまだまだ模索中です。
もう少し触って色々確かめてみたいと思います。