Android - Bottom Navigation View


Android - Bottom Navigation View

Overview

一直以來,關於Android的底部導航的功能實現的方法一直是各行其道不成規范,使用各種方法的都有

  • RadioButton
  • TextView
  • ...

在Material Design 中推出了這樣的一個控件來解決底部導航欄的不統一的問題,但是這個控件有一點點的問題...

問題所在

現在的效果非常棒...

結果一旦Item的數量超過了3個,就會有一個非常鬼畜的動畫效果,讓人無法接受。

如何使用

在menu目錄下新建一個menu菜單,這個菜單將會用於生成BottomNavigationView控件的item

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navItem_UI"
        android:icon="@drawable/ui"
        android:title="UI" />
    <item
        android:id="@+id/navItem_data"
        android:icon="@drawable/data"
        android:title="Data" />
    <item
        android:id="@+id/navItem_API"
        android:icon="@drawable/api"
        android:title="API" />
</menu>

xml 布局文件

<android.support.design.widget.BottomNavigationView
    android:id="@+id/nav"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#f6f6f6"
    app:itemTextColor="@color/colorPrimary"
    app:itemIconTint="@color/colorPrimary"
    app:menu="@menu/nav_menu"/>

主要屬性

  • itemTextColor 字體的顏色
  • itemIconTint 圖標的顏色
  • menu 綁定的菜單

處理事件

void registerEvent() {
    //設置Item的點擊事件
    navView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            CommonUtil.toast(item.getTitle().toString());
            return true;
        }
    });
}

干掉那個浮誇的動畫

效果圖

一直以為Google會提供相應的方法,但是找了半天都沒找到,但是我們有源碼啊,只要溜進去控件的內部,看一看是怎么回事了。

/**
在源碼中,返現,這個控件是基於MVP架構的,去MVP架構的View中找
*/

private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};

private static final int MENU_PRESENTER_ID = 1;

private final MenuBuilder mMenu;

//這個字段是MVP架構中的View,點進去他的源碼中尋找
private final BottomNavigationMenuView mMenuView;
private final BottomNavigationPresenter mPresenter = new BottomNavigationPresenter();
private MenuInflater mMenuInflater;

private OnNavigationItemSelectedListener mSelectedListener;
private OnNavigationItemReselectedListener mReselectedListener;

進入BottomNavigationView中繼續尋找

@RestrictTo(LIBRARY_GROUP)
public class BottomNavigationMenuView extends ViewGroup implements MenuView {
    private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;

    private final TransitionSet mSet;
    private final int mInactiveItemMaxWidth;
    private final int mInactiveItemMinWidth;
    private final int mActiveItemMaxWidth;
    private final int mItemHeight;
    private final OnClickListener mOnClickListener;
    private final Pools.Pool<BottomNavigationItemView> mItemPool = new Pools.SynchronizedPool<>(5);
	//找到了一個比較可疑的字段, shifting 有道翻譯一下-> 位移 關閉動畫應該就是這個了
  	//這個字段是關閉的控件的位移動畫
    private boolean mShiftingMode = true;
	//這個是BottomNavigationMenuView 中的各個item,進入繼續找
    private BottomNavigationItemView[] mButtons;
    private int mSelectedItemId = 0;
    private int mSelectedItemPosition = 0;
    private ColorStateList mItemIconTint;
    private ColorStateList mItemTextColor;
    private int mItemBackgroundRes;
    private int[] mTempChildWidths;

    private BottomNavigationPresenter mPresenter;
    private MenuBuilder mMenu;

進入到BottomNavigationMenuView 中繼續尋找

@RestrictTo(LIBRARY_GROUP)
public class BottomNavigationItemView extends FrameLayout implements MenuView.ItemView {
    public static final int INVALID_ITEM_POSITION = -1;

    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };

    private final int mDefaultMargin;
    private final int mShiftAmount;
    private final float mScaleUpFactor;
    private final float mScaleDownFactor;
	//這里還有一個動畫效果的設置, 不過這個有方法來供我們設置,不同通過反射來設置了
  	//這個字段標識的是,圖標上移的動畫,如果不關閉這個動畫,那么如果ItemNavigationView的Item超過了3個那么
  	//只有選中了的Item才會顯示文字,而其他的不顯示文字
    private boolean mShiftingMode;

    private ImageView mIcon;
    private final TextView mSmallLabel;
    private final TextView mLargeLabel;
    private int mItemPosition = INVALID_ITEM_POSITION;

    private MenuItemImpl mItemData;

    private ColorStateList mIconTint;
找到了標志屬性,那就該我們的反射出場了了
void initNav() {
    try {
      	//在這里為什么使用getChildAt(0),的線索可以在BottomNavigationMenuView的構造方法中找到線索
        BottomNavigationMenuView menuView = (BottomNavigationMenuView) navView.getChildAt(0);
        Field field = menuView.getClass().getDeclaredField("mShiftingMode");
      	//取消位移動畫
        field.setAccessible(true);
        field.setBoolean(menuView, false);
        //遍歷所有的Item取消上移動畫
        for (int i = 0; i < navView.getChildCount(); i++) {
          	//這里為什么調用的是getChildAt(i); 可以在BottomNavigationMenuView的buildMenuView方法中找到線索
            BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
            itemView.setShiftingMode(false);
        }
      	////更新一下MenuView,如果不加這句代碼,初始化的是時候會一個顯示樣式的小bug出現
      	menuView.updateMenuView();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在初始化控件后調用此方法就可以達到我們想要的效果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM