http://blog.magicer.xyz/2017/...
BottomNavitagionView
出现之前,我们首先底部的导航栏大多是使用RadioGroup
+RadioButton
的特性来实现。现在官方为我们提供了另外的一条路。先来尝试下。
Demo
打开官方文档里面就有示例程序,比着敲一遍,看下效果。
<android.support.design.widget.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
android:layout_gravity="center"
android:gravity="center"
app:itemIconTint="#009877"
app:itemTextColor="#009877"
app:paddingStart="10dp"
app:paddingEnd="10dp"
app:itemBackground="@color/white"
app:menu="@menu/bottom_nav"/>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_nav_android"
android:icon="@drawable/ic_android_black_36dp"
app:showAsAction="ifRoom"
android:title="Android"/>
<item
android:id="@+id/menu_nav_lock"
android:icon="@drawable/ic_lock_black_36dp"
app:showAsAction="ifRoom"
android:title="Lock"/>
<item
android:id="@+id/menu_nav_group_work"
android:icon="@drawable/ic_group_work_black_36dp"
app:showAsAction="ifRoom"
android:title="Work"/>
</menu>
效果如下
嗯。效果还是不错的。但是有时候我们需要的是颜色的变化,这个时候我们需要一个color
。在res
下的color
文件夹下创建一个文件名为bottom_nav
的文件内容如下。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:color="@color/nav_checked"
android:state_checked="true"/>
<item
android:color="@color/nav_unchecked"
android:state_checked="false"/>
</selector>
之后为BottomNavitagionView
设置上就可以了。
效果如下
app:itemIconTint="@color/bottom_nav"
app:itemTextColor="@color/bottom_nav"
问题
真的就这么就完了么? too young,too simple。我们底部的菜单项肯定不一定是3个。如果是4个。我们试一下,再加菜单项。很简单为menu
再添加个item
。来看下效果:
WTF
来看下源码看看到底发生了什么?BottomNavigationView
的源码
private final MenuBuilder mMenu;
private final BottomNavigationMenuView mMenuView;
private final BottomNavigationPresenter mPresenter = new BottomNavigationPresenter();
private MenuInflater mMenuInflater;
//.....省略代码
public void inflateMenu(int resId) {
mPresenter.setUpdateSuspended(true);
getMenuInflater().inflate(resId, mMenu);
mPresenter.setUpdateSuspended(false);
mPresenter.updateMenuView(true);
}
我们看到。其中主要有这几个属性,通过查看代码我们可以看到。BottomNavigationPresenter
来连接Menu
和BottomNavigationMenuView
的。BottomNavigationPresenter
的部分代码
private MenuBuilder mMenu;
private BottomNavigationMenuView mMenuView;
@Override
public void initForMenu(Context context, MenuBuilder menu) {
mMenuView.initialize(mMenu);
mMenu = menu;
}
....省略...
@Override
public void updateMenuView(boolean cleared) {
if (mUpdateSuspended) return;
if (cleared) {
mMenuView.buildMenuView();
} else {
mMenuView.updateMenuView();
}
}
我们可以看到。更新MenuView
的方法是通过Presenter
来调用的。其内部的代码
public void buildMenuView() {
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);
}
mActiveButton = Math.min(mMenu.size() - 1, mActiveButton);
mMenu.getItem(mActiveButton).setChecked(true);
}
这里我们应该就看出来端倪了。mShiftingMode
是个boolean
值,当menu
的长度大于三时,就为true
。也就为每一个BottomNavigationItemView
设置上了child.setShiftingMode(mShiftingMode);
true。在这里每一个BottomNavigationItemView
就是一个tab
。
在BottomNavigationItemView
的代码中我们可以看到。其填充的布局为R.layout.design_bottom_navigation_item
布局中有两个TextView
(smallLabel和largeLabel) 和一个ImageView
(icon),详细的代码自己搜一下吧。在design
包中,这里就不贴出来了。
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);
}
}
啊哈。看了这么多终于找到原因了。 这就是问题所在。我们见到如果我们吧mShiftingMode
设置为false
那么就不会出现那种效果。怎么设置呢。我们可以使用反射的机制来进行设置。
代码如下
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);
item.setShiftingMode(false);
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。