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();
}
}
在初始化控件后調用此方法就可以達到我們想要的效果