http://blog.magicer.xyz/2017/...html
BottomNavitagionView
出現以前,咱們首先底部的導航欄大可能是使用RadioGroup
+RadioButton
的特性來實現。如今官方爲咱們提供了另外的一條路。先來嘗試下。java
打開官方文檔裏面就有示例程序,比着敲一遍,看下效果。android
<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>
效果以下app
嗯。效果仍是不錯的。可是有時候咱們須要的是顏色的變化,這個時候咱們須要一個color
。在res
下的color
文件夾下建立一個文件名爲bottom_nav
的文件內容以下。ide
<?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
。來看下效果:ui
WTFspa
來看下源碼看看到底發生了什麼?BottomNavigationView
的源碼code
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
的部分代碼xml
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(); } }