Android Design Library之二: BottomNavigationView

http://blog.magicer.xyz/2017/...html

BottomNavitagionView出現以前,咱們首先底部的導航欄大可能是使用RadioGroup+RadioButton的特性來實現。如今官方爲咱們提供了另外的一條路。先來嘗試下。java

Demo

打開官方文檔裏面就有示例程序,比着敲一遍,看下效果。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

clipboard.png

嗯。效果仍是不錯的。可是有時候咱們須要的是顏色的變化,這個時候咱們須要一個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設置上就能夠了。
效果以下佈局

clipboard.png

app:itemIconTint="@color/bottom_nav"
        app:itemTextColor="@color/bottom_nav"

問題

真的就這麼就完了麼? too young,too simple。咱們底部的菜單項確定不必定是3個。若是是4個。咱們試一下,再加菜單項。很簡單爲menu再添加個item。來看下效果:ui

clipboard.png

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來鏈接MenuBottomNavigationMenuView的。
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();
        }
    }

clipboard.png

相關文章
相關標籤/搜索