はじめに
FiNCという会社でAndroidのアプリ開発を行っているんですが、
ドロワーにある機能に対してユーザーの遷移率が芳しくありませんでした。
僕自身Android歴が長いので違和感を感じませんでした。
よくよく考えてみると、かの有名なSNSアプリなどを利用する際にも、
自分はドロワーを利用する頻度はほとんどないなということに気づきました。
現状のドロワーの実装を進展させるために新しくリリースされた、
BottomNavigationViewを実装してみました。
BottomNavigationViewとは
これです。
この役割ってなんなの?というところなんですが、公式ドキュメント曰く、
Bottom navigation bars make it easy to explore and switch between top-level views in a single tap.
公式ドキュメント
ワンタップで、トップレベルのビューの行き来できる魔法のようなものらしいです。
iOSとかでいうグローバルナビゲーションですね。(今更感が否めないですが)
自分の記憶が正しければ、マテリアルデザインガイドラインには、2016年の5, 6月?くらいには記載されていたと思うのですが、実際にリリースされるプロダクトのAPIとして提供されておりませんでした。
そのAPIが先日リリースされました。
support library revision history
BottomNavigationViewの構成クラス群
- a.s.d.widget.BottomNavigationView (FrameLayout)
- a.s.d.internal.BottomNavigationPresenter
- a.s.d.internal.BottomNavigationMenuView (ViewGroup)
- a.s.d.internal.BottomNavigationItemView (FrameLayout)
BottomNavigationViewを構成するクラスは主に上記なんですが、実際に利用する一番上のクラス以外はinternalで直接扱うことは恐らくないです。
このクラス群はMVPの構造になっているようです。(後ほど記載)
具体的な実装ステップ
ちゃっかり実装自体は、とても簡単です。
- Step1: menu.xmlを作成する
- Step2: layout.xmlを作成する
- Step3: listenerをセットする。
・Steps1
build.gradleでsupport libraryのバージョンを最新に
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の実装と同じようにする。
<?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のサブクラスです。
<!-- 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()とかで切り分けするのがいいのかも。)
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でその部分を指定します。(完成したらアップします。以下の内部を実装する予定。)
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で上手く切り分けられていない気がしています。
<CoordinatorLayout>
<AppBarLayout>
<Toolbar />
</AppBarLayout>
<FrameLayout /> <!-- ここにページごとのFragmentをセットする。 -->
<BottomNavigationView />
</CoordinatorLayout>
おまけ
BottomNavigationViewはMVPで実装されています。
BottomNavigationViewは内部にPresenterを保持していて、そのPresenterがMenuとMenuItemをmenu.xmlからパース(モデル化)して、各viewに繋ぎこんでいます。
所感
BottomNavigationViewというくらいなので、Viewとしての役割を意識させたかったのかなと思っています。
なので、Fragmentに入れちゃうのもいいのかななんて考えています。
はじめはガイドライン読んでなかったので、
BottomNavigationView + ViewPagerのようにSwipeでページングするものとの実装を行っていました。
(これはダメな実装です。)
BottomNavigationViewで移動できるのはアイテムをクリックした時のみです。
少し触っただけなので、実際に実装する際のベストプラクティスはまだまだ模索中です。
もう少し触って色々確かめてみたいと思います。