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>