78
56

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

O-DEV(おで部)Advent Calendar 2017

Day 10

Android BottomNavigationViewのカスタマイズ

Last updated at Posted at 2017-12-10

背景

2017年9月ごろ、AndroidでWebViewベースのガワネイティブアプリを実装する機会があり、WebViewのナビゲーション部分をBottomNavigationView実装してみた。そのときBottomNavigationViewのデザイン調整で苦労したのでまとめてみた。

デザインの課題

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

実装時のビルドツール、コンパイルSDK、ターゲットSDK

compileSdkVersion 25
buildToolsVersion "25.0.2"

minSdkVersion 19
targetSdkVersion 25

目次

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

事前準備

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

まず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>

元記事:https://blog.local-c.com/archives/2073

78
56
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
78
56

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?