Android Design Support Library系列之六:BottomNavigationView的使用

底部导航栏的使用非常广泛,在Android Design Support Library25,google推出了Android的底部导航栏------BottomNavigationView.

BottomNavigationView指南
BottomNavigationView官方文档

一、BottomNavigationView简单使用

使用前添加依赖

compile 'com.android.support:design:25.3.1'

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"

        app:menu="@menu/bottom_navigation"> 

    </android.support.design.widget.BottomNavigationView>
</RelativeLayout>

NavigationView的使用差不多,BottomNavigationView的内容部分定义在菜单文件夹下,然后通过 app:menu=" "属性引用该菜单文件即可。
res/menu/bottom_navigation.xml

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

    <item
        android:id="@+id/item_1"
        android:icon="@mipmap/p1"
        android:title="消息" />

    <item
        android:id="@+id/item_2"
        android:icon="@mipmap/p2"
        android:title="联系人" />

    <item
        android:id="@+id/item_3"
        android:icon="@mipmap/p3"
        android:title="动态" />

</menu>

效果图

二、BottomNavigationView的常用属性

1、图标颜色修改

上面三张是图标原图,而根据效果图可以发现:选中和未被选中时的颜色都是主题中的,并未使用图标本身颜色,我们可以通过下面这条属性修改图标颜色

app:itemIconTint=" "

(1)这里的图标颜色可以是单一的,选中未选中都是一个颜色,像这样:

 app:itemIconTint="@android:color/holo_blue_light"

(2)也可以给图标颜色添加一个选择器效果,像这样:

app:itemIconTint="@color/seletor_bottom_navi"

颜色选择器:res/color/seletor_bottom_navi.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@android:color/holo_blue_light" 
        android:state_checked="true" />
    <item android:color="@android:color/black" />
</selector>

(3)当然,你也可以恢复图标本身颜色,需要在代码中设置

mBottomNV = (BottomNavigationView) findViewById(R.id.bottom_navigation);
mBottomNV.setItemIconTintList(null);

(4)如果你想给图标也添加一个选择器效果,你可以在菜单文件中引用图片时引用一个seletor

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

    <item
        android:id="@+id/item_1"
        android:icon="@drawable/seletor_item1"      
        android:title="消息" />

    <item
        android:id="@+id/item_2"
        android:icon="@mipmap/p2"
        android:title="联系人" />

    <item
        android:id="@+id/item_3"
        android:icon="@mipmap/p3"
        android:title="动态" />
</menu>

图片选择器:res/drawable/seletor_item1.xml

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

    <item android:drawable="@mipmap/p1" android:state_checked="true"></item>
    <item android:drawable="@mipmap/p4" ></item>

</selector>
2、字体颜色修改

字体颜色默认也是使用系统主题中的,我们可以通过下面这条属性修改:

app:itemTextColor=" "

你可以使用单一的,也可以添加一个选择器效果

app:itemTextColor="@android:color/holo_blue_light"
app:itemTextColor="@color/seletor_bottom_navi"
3、背景颜色修改
app:itemBackground=""

当然,你也可以直接通过android:background=" "修改背景颜色

三、BottomNavigationView常用方法

设置图标颜色
setItemIconTintList()
设置字体颜色
setItemTextColor()
设置背景
setBackgroundColor()      需要一个颜色值
setItemBackgroundResource()   需要一个资源id,可以是颜色、图片、shape
setBackground()  需要一个drawable对象,minSdkVersion>=16才可以用
设置默认选中哪个item
setSelectedItemId()
设置监听
setOnNavigationItemSelectedListener()

        mBottomNV.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.item_1:
                     //do something
                        break;
                    case R.id.item_2:
                     //do something
                        break;
                    case R.id.item_3:
                     //do something
                        break;
                }
                return true;
            }
        });

四、BottomNavigationView使用规范

1)官方建议菜单item个数为 3~5 个
我尝试6个item,直接报错:


于是查看BottomNavigationView源码:

   public int getMaxItemCount() {
        return BottomNavigationMenu.MAX_ITEM_COUNT;
    }

追踪到BottomNavigationMenu:

 public static final int MAX_ITEM_COUNT = 5;

ok,完全了解了.
我又尝试2个,没有报错,不过2个的话还是不建议使用BottomNavigationView
2)底部导航栏高度建议是56dp
更多规范请查看官网BottomNavigationView指南

五、关于BottomNavigationView的ShiftingMode

(1)上面我们一直使用三个item,下面我们使用4个item试一试

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

    <item
        android:id="@+id/item_1"
        android:icon="@mipmap/p1"
        android:title="消息" />

    <item
        android:id="@+id/item_2"
        android:icon="@mipmap/p2"
        android:title="联系人" />
    <item
        android:id="@+id/item_3"
        android:icon="@mipmap/p3"
        android:title="动态" />
    <item
        android:id="@+id/item_4"
        android:icon="@mipmap/p4"
        android:title="我的" />
</menu>

效果图


可以看见,当item数量为4个的时候,效果和之前完全不一样了。。。。。。
(2)查看源码,最终追踪到:

 public void buildMenuView() {
        removeAllViews();
        if (mButtons != null) {
            for (BottomNavigationItemView item : mButtons) {
                mItemPool.release(item);
            }
        }
        if (mMenu.size() == 0) {
            mSelectedItemId = 0;
            mSelectedItemPosition = 0;
            mButtons = null;
            return;
        }
        mButtons = new BottomNavigationItemView[mMenu.size()];
        mShiftingMode = mMenu.size() > 3;//❤❤❤❤❤
        for (int i = 0; i < mMenu.size(); i++) {
            mPresenter.setUpdateSuspended(true);
            mMenu.getItem(i).setCheckable(true);
            mPresenter.setUpdateSuspended(false);
            BottomNavigationItemView child = getNewItem();
            mButtons[i] = child;
            child.setIconTintList(mItemIconTint);
            child.setTextColor(mItemTextColor);
            child.setItemBackground(mItemBackgroundRes);
            child.setShiftingMode(mShiftingMode);//❤❤❤❤❤
            child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
            child.setItemPosition(i);
            child.setOnClickListener(mOnClickListener);
            addView(child);
        }
        mSelectedItemPosition = Math.min(mMenu.size() - 1, mSelectedItemPosition);
        mMenu.getItem(mSelectedItemPosition).setChecked(true);
    }
 public void setShiftingMode(boolean enabled) {
        mShiftingMode = enabled;
 }
    public void setChecked(boolean checked) {
        ViewCompat.setPivotX(mLargeLabel, mLargeLabel.getWidth() / 2);
        ViewCompat.setPivotY(mLargeLabel, mLargeLabel.getBaseline());
        ViewCompat.setPivotX(mSmallLabel, mSmallLabel.getWidth() / 2);
        ViewCompat.setPivotY(mSmallLabel, mSmallLabel.getBaseline());
        if (mShiftingMode) {//❤❤❤❤❤
            if (checked) {
                LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
                iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
                iconParams.topMargin = mDefaultMargin;
                mIcon.setLayoutParams(iconParams);
                mLargeLabel.setVisibility(VISIBLE);
                ViewCompat.setScaleX(mLargeLabel, 1f);
                ViewCompat.setScaleY(mLargeLabel, 1f);
            } else {
                LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
                iconParams.gravity = Gravity.CENTER;
                iconParams.topMargin = mDefaultMargin;
                mIcon.setLayoutParams(iconParams);
                mLargeLabel.setVisibility(INVISIBLE);
                ViewCompat.setScaleX(mLargeLabel, 0.5f);
                ViewCompat.setScaleY(mLargeLabel, 0.5f);
            }
            mSmallLabel.setVisibility(INVISIBLE);
        } else {//❤❤❤❤❤
            if (checked) {
                LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
                iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
                iconParams.topMargin = mDefaultMargin + mShiftAmount;
                mIcon.setLayoutParams(iconParams);
                mLargeLabel.setVisibility(VISIBLE);
                mSmallLabel.setVisibility(INVISIBLE);

                ViewCompat.setScaleX(mLargeLabel, 1f);
                ViewCompat.setScaleY(mLargeLabel, 1f);
                ViewCompat.setScaleX(mSmallLabel, mScaleUpFactor);
                ViewCompat.setScaleY(mSmallLabel, mScaleUpFactor);
            } else {
                LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
                iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
                iconParams.topMargin = mDefaultMargin;
                mIcon.setLayoutParams(iconParams);
                mLargeLabel.setVisibility(INVISIBLE);
                mSmallLabel.setVisibility(VISIBLE);

                ViewCompat.setScaleX(mLargeLabel, mScaleDownFactor);
                ViewCompat.setScaleY(mLargeLabel, mScaleDownFactor);
                ViewCompat.setScaleX(mSmallLabel, 1f);
                ViewCompat.setScaleY(mSmallLabel, 1f);
            }
        }
        refreshDrawableState();
    }

好吧,item数量大于3的时候会开启ShiftingMode,效果和之前完全不一样了......
这种效果简直难以忍受,而且目前还没有一个属性或者方法能够修改ShiftingMode.

(3) 通过反射修改ShiftingMode
准备用反射来修改ShiftingMode值,结果发现stackoverflow上早已经有外国小哥写好了,直接copy就可以了~~~

/**
 * 新建一个BottomNavigationView帮助者类
 */
public class BottomNavigationViewHelper {
    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 item = (BottomNavigationItemView) menuView.getChildAt(i);
                //noinspection RestrictedApi
                item.setShiftingMode(false);
                // set once again checked value, so view will be updated
                //noinspection RestrictedApi
                item.setChecked(item.getItemData().isChecked());
            }
        } catch (NoSuchFieldException e) {
            Log.e("BNVHelper", "Unable to get shift mode field", e);
        } catch (IllegalAccessException e) {
            Log.e("BNVHelper", "Unable to change value of shift mode", e);
        }
    }
}

然后调用即可

BottomNavigationViewHelper.disableShiftMode(mBottomNV);

最后,你需要在混淆文件中配置

-keepclassmembers class android.support.design.internal.BottomNavigationMenuView { 
    boolean mShiftingMode; 
}

(4) 效果图


ok,完美解决

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,439评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,357评论 0 17
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,705评论 22 664
  • 我走在成长路上 成长路上不乏泪水 沿途的琐事是一滴 心头的雾水亦是一滴 即便如此 我在鸡汤中看到的泪点 化为滋润着...
    倾听你的倾听阅读 296评论 0 0