Android BottomNavigationViewのカスタマイズ

BottomNavigationViewは、アイテムが3つまでは均等サイズのアイテムが並びます。しかしアイテムが4つ以上になると選択中のアイテムだけアイコン下部にテキストが表示されたり、アニメーション、エフェクトが効いて選択中のアイテムだけが強調されたりする。どうしてもアプリの仕様で4つのアイテムを均等に並べたいので、いろいろなサイトを調査し、BottomNavigationViewをカスタマイズしていきたい。

目次

  • まずBottomNavigationViewを追加
  • 4つのアイテム(BottomNavigationItemView)を均等の大きさで並べる
  • アイテム(BottomNavigationItemView)のテキストを非表示にする
  • アイテム(BottomNavigationItemView)のアイコンサイズを大きくする
  • アイテム(BottomNavigationItemView)を選択したときのアニメーション?、エフェクト?を無効にする
  • アイテム(BottomNavigationItemView)をタップできないようにアイテムを無効にする

事前準備

新規プロジェクト作成時にアプリの雛形で選択できる「Navigation Drawer Activity」の雛形を使用して説明します。

まずBottomNavigationViewを追加

BottomNavigationViewで表示するアイテムを作成する

「bottom_navigation_item.xml」を新規作成し、4つのアイテムを適当に作る(アイコンはandroid sdk デフォルトのアイコン)

/MyApp/app/src/main/res/menu/bottom_navigation_item.xml


<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/nav_camera" android:icon="@drawable/ic_menu_camera" android:title="Import"/> <item android:id="@+id/nav_gallery" android:icon="@drawable/ic_menu_gallery" android:title="Gallery"/> <item android:id="@+id/nav_slideshow" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow"/> <item android:id="@+id/nav_manage" android:icon="@drawable/ic_menu_manage" android:title="Tools"/> </menu>

BottomNavigationViewのアイテムを選択したときのホバー的な設定

「bottom_navigation_item_state.xml」を新規作成しアイテムが状態によって色を変更するようにする。

/MyApp/app/src/main/res/drawable/bottom_navigation_item_state.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- チェック状態 -->
    <item android:color="?colorAccent" android:state_checked="true"/>
    <!-- タップしたとき -->
    <item android:color="?colorPrimaryDark" android:state_pressed="true"/>
    <!-- 通常 -->
    <item android:color="#FFFFFF"/>
</selector>

「「app_bar_main.xml」へBottomNavigationViewを追加する

「app_bar_main.xml」はプロジェクト作成で自動で出来るファイルに「android.support.design.widget.BottomNavigationView」タグを追加する。
「app:menu」には、先ほど作成した「bottom_navigation_item.xml」を設定し、「app:itemIconTint」に「navigation_item_state.xml」を設定する。

/MyApp/app/src/main/res/layout/app_bar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    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="com.myapp.myapp.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="80dp"
        app:srcCompat="@android:drawable/ic_dialog_email"/>

    <!-- BottomNavigationView を追加-->
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="?actionBarSize"
        android:layout_gravity="bottom"
        android:background="@color/colorPrimary"
        app:itemTextColor="@drawable/bottom_navigation_item_state"
        app:itemIconTint="@drawable/bottom_navigation_item_state"
        app:menu="@menu/bottom_navigation_item"/>

</android.support.design.widget.CoordinatorLayout>

その他に、「android.support.design.widget.FloatingActionButton」のメールのボタンが邪魔なので、配置場所を調整してます。

android:layout_marginRight="16dp"
android:layout_marginBottom="80dp"

4つ以上のアイテムを均等の大きさで並べる

4つ以上のアイテムを均等に並べるにはレイアウトをごにょごにょする必要があるが、今回はBottomNavigationViewを継承してBottomNavigationViewの独自クラスをするのではなく
「BottomNavigationViewHelper」というヘルパークラスを作成してBottomNavigationViewを作るときにレイアウトを調整する。

BottomNavigationViewHelperを新規に作成する

package com.myapp.myapp;

import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;

import java.lang.reflect.Field;

public class BottomNavigationViewHelper {

    /**
     * BottomNavigationViewのアイテムのサイズの調整、アイコンサイズ調整、タイトルの削除
     *
     * @param view
     */
    public static void disableShiftMode(BottomNavigationView view) {
        BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
        try {

            Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
            shiftingMode.setAccessible(true);
            shiftingMode.setBoolean(menuView, false);
            shiftingMode.setAccessible(false);

            for (int i = 0; i < menuView.getChildCount(); i++) {

                /**
                 * アイテムの幅調整
                 */
                BottomNavigationItemView bottomNavigationItemView = (BottomNavigationItemView) menuView.getChildAt(i);
                // noinspection RestrictedApi
                bottomNavigationItemView.setShiftingMode(false);
                // チェックされた値を設定すると、ビューが更新されるみたい
                // noinspection RestrictedApi
                bottomNavigationItemView.setChecked(false);

            }

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}


ActivityからBottomNavigationViewHelperを呼びす

onCreateでBottomNavigationViewHelper#disableShiftModeへBottomNavigationViewを渡し調整、その後ついでに選択のリスナーを実装

// ボトムナビゲーションを読み込む
        BottomNavigationView bottomavigation = (BottomNavigationView) findViewById(R.id.bottom_navigation);
        // BottomNavigationViewHelperでアイテムのサイズ、アニメーションを調整
        BottomNavigationViewHelper.disableShiftMode(bottomavigation);
        // BottomNavigationViewを選択したときのリスナー
        bottomavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {

                // 各選択したときの処理
                switch (item.getItemId()) {
                    case R.id.nav_camera:

                        return true;
                    case R.id.nav_gallery:

                        return true;
                    case R.id.nav_slideshow:

                        return true;
                    case R.id.nav_manage:

                        return true;
                }
                return false;
            }
        });

アイテム(BottomNavigationItemView)のテキストを非表示にする

BottomNavigationItemViewには未選択時のテキストのレイアウト「android.support.design.R.id.smallLabel」と選択中の大きめのテキスト「android.support.design.R.id.largeLabel」が存在する。テキストが全て不要だったので、親のレイアウトごと削除した。
BottomNavigationViewHelper#disableShiftModeのfor文の中に以下を追加して、常にテキストを非表示にした。


/** * アイテムのテキストを非表示にする。 * アイテムのテキストビューをくくってるBaselineLayoutをGONE */ final View smallLabel = menuView.getChildAt(i).findViewById(android.support.design.R.id.smallLabel); BaselineLayout baselineLayout = (BaselineLayout) smallLabel.getParent(); baselineLayout.setVisibility(View.GONE);

アイテム(BottomNavigationItemView)のアイコンサイズを大きくする

BottomNavigationViewHelper#disableShiftModeのfor文の中に以下を追加して、アイコンサイズを大きくした

/**
 * アイコンサイズを40dpに調整
 */
final View iconView = menuView.getChildAt(i).findViewById(android.support.design.R.id.icon);
final ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
final DisplayMetrics displayMetrics = view.getResources().getDisplayMetrics();
layoutParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, displayMetrics);
layoutParams.width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, displayMetrics);
iconView.setLayoutParams(layoutParams);

アイテム(BottomNavigationItemView)を選択したときのアニメーション?、エフェクト?を無効にする

選択すると少しアイコンが上部に移動するアニメーションは、チェックされた状態になる。この効果をなくすには、ずっと未チェック状態にすればいいと思い。
常にチェックされないよう修正した。

BottomNavigationViewHelper#disableShiftModeのfor文の中に以下を追加すると全部のアイテムがチェックしていない状態に出来る

// noinspection RestrictedApi
bottomNavigationItemView.setEnabled(false);

またタップ(選択)したときのリスナーの戻り値もfalseにする

// BottomNavigationViewを選択したときのリスナー
        bottomavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {

                // 各選択したときの処理
                switch (item.getItemId()) {
                    case R.id.nav_camera:

                        return false;  // falseを返す
                    case R.id.nav_gallery:

                        return false;  // falseを返す
                    case R.id.nav_slideshow:

                        return false;  // falseを返す
                    case R.id.nav_manage:

                        return false;  // falseを返す
                }
                return false;
            }
        });


アイテム(BottomNavigationItemView)をタップできないようにアイテムを無効にする

アイテムの選択が無効状態にできる。

// noinspection RestrictedApi
bottomNavigationItemView.setChecked(false);

無効状態のアイコンの色も設定する

/MyApp/app/src/main/res/drawable/bottom_navigation_item_state.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 無効状態 -->
    <item android:state_enabled="false" android:color="?colorPrimaryDark"/>
    <!-- 有効状態 -->
    <item android:state_enabled="true" android:color="#FFFFFF"/>
    <!-- チェック状態 -->
    <item android:color="?colorAccent" android:state_checked="true"/>
    <!-- タップしたとき -->
    <item android:color="?colorPrimaryDark" android:state_pressed="true"/>
    <!-- 通常 -->
    <item android:color="#FFFFFF"/>
</selector>