android:
id="@+id/menu_navi"
android:
icon="@drawable/ic_navigation_black_24dp"
android:
title="@string/menu_navigation"/>
与定义普通menu布局一样。
接下来是java代码
bottomNaviView=(BottomNavigationView)findViewById(R.id.bottom_navi_view);
bottomNaviView.setOnNavigationItemSelectedListener(newBottomNavigationView.OnNavigationItemSelectedListener(){
@Override
publicbooleanonNavigationItemSelected(@NonNullMenuItemitem){
switch(item.getItemId()){
caseR.id.menu_recent:
break;
caseR.id.menu_favorites:
break;
caseR.id.menu_nearby:
break;
caseR.id.menu_navi:
break;
}
returntrue;
}
});
对BottomNavigationView设置选择监听器就可以做一些item切换事件了。
注意事项
底部导航栏默认高度是56dp
菜单只能是3-5个
源码分析
BottomNavigationView有几个先关的重要类
BottomNavigationView
BottomNavigationMenu
BottomNavigationMenuView
BottomNavigationPresenter
它的设计有点类似于开发中的MVP模式。
先来看BottomNavigationView的构造函数
publicBottomNavigationView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
ThemeUtils.checkAppCompatTheme(context);//检测当前主题
//Createthemenu
mMenu=newBottomNavigationMenu(context);
mMenuView=newBottomNavigationMenuView(context);
FrameLayout.LayoutParamsparams=newFrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity=Gravity.CENTER;
mMenuView.setLayoutParams(params);
mPresenter.setBottomNavigationMenuView(mMenuView);
mMenuView.setPresenter(mPresenter);
mMenu.addMenuPresenter(mPresenter);
//Customattributes
//...省略若干代码
if(a.hasValue(R.styleable.BottomNavigationView_menu)){
inflateMenu(a.getResourceId(R.styleable.BottomNavigationView_menu,0));//加载menu
}
a.recycle();
addView(mMenuView,params);//
mMenu.setCallback(newMenuBuilder.Callback(){//设置监听器
@Override
publicbooleanonMenuItemSelected(MenuBuildermenu,MenuItemitem){
returnmListener!
=null&&mListener.onNavigationItemSelected(item);
}
@Override
publicvoidonMenuModeChange(MenuBuildermenu){}
});
}
ThemeUtils.checkAppCompatTheme(context)是用来检测当前主题的。
代码很简单,就是判断是否有colorPrimary属性。
classThemeUtils{
privatestaticfinalint[]APPCOMPAT_CHECK_ATTRS={
android.support.v7.appcompat.R.attr.colorPrimary
};
staticvoidcheckAppCompatTheme(Contextcontext){
TypedArraya=context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);
finalbooleanfailed=!
a.hasValue(0);
if(a!
=null){
a.recycle();
}
if(failed){
thrownewIllegalArgumentException("YouneedtouseaTheme.AppCompattheme"
+"(ordescendant)withthedesignlibrary.");
}
}
}
构造函数中接下来调用了inflateMenu()
publicvoidinflateMenu(intresId){
mPresenter.setUpdateSuspended(true);
getMenuInflater().inflate(resId,mMenu);
mPresenter.initForMenu(getContext(),mMenu);
mPresenter.setUpdateSuspended(false);
mPresenter.updateMenuView(true);
}
可以看出都是调用的BottomNavigationPresenter的函数。
setUpdateSuspended(true)–暂停修改menu
setUpdateSuspended(false)—可以修改menu
在initForMenu()一前一后,设置一个标志来表示当前正在操作menu。
重点来看BottomNavigationPresenter.initForMenu()
@Override
publicvoidinitForMenu(Contextcontext,MenuBuildermenu){
mMenuView.initialize(mMenu);
mMenu=menu;
}
函数内部又是调用的BottomNavigationMenuView.initialize()
@Override
publicvoidinitialize(MenuBuildermenu){
mMenu=menu;
if(mMenu==null)return;
if(mMenu.size()>mActiveButton){
mMenu.getItem(mActiveButton).setChecked(true);
}
}
代码中就是一些简单的初始化操作。
接下来看BottomNavigationPresenter.updateMenuView(true)
@Override
publicvoidupdateMenuView(booleancleared){
if(mUpdateSuspended)return;
if(cleared){
mMenuView.buildMenuView();
}else{
mMenuView.updateMenuView();
}
}
第一次创建Menu时,调用的是buildMenuView方法。
BottomNavigationMenuView.buildMenuView()
publicvoidbuildMenuView(){
if(mButtons!
=null){
for(BottomNavigationItemViewitem:
mButtons){
sItemPool.release(item);
}
}
removeAllViews();
mButtons=newBottomNavigationItemView[mMenu.size()];
//当menuitem大于3个的时候,会出现缩放动画
mShiftingMode=mMenu.size()>3;
for(inti=0;imPresenter.setUpdateSuspended(true);
mMenu.getItem(i).setCheckable(true);
mPresenter.setUpdateSuspended(false);
BottomNavigationItemViewchild=getNewItem();
mButtons[i]=child;
child.setIconTintList(mItemIconTint);
child.setTextColor(mItemTextColor);
child.setItemBackground(mItemBackgroundRes);
child.setShiftingMode(mShiftingMode);
//单个item--BottomNavigationItenView的初始化操作
child.initialize((MenuItemImpl)mMenu.getItem(i),0);
child.setItemPosition(i);
//设置点击事件
child.setOnClickListener(mOnClickListener);
//添加子视图
addView(child);
}
}
在BottomNavigationMenuView类中定义了一个Pool对象,用来缓存BottomNavigationItemView对象。
privatestaticfinalPools.PoolsItemPool=newPools.SynchronizedPool<>(5);
通过for循环创建了nMenu.size()个BottomNavigationItemView对象。
BottomNavigationItemViewchild=getNewItem();
mButtons[i]=child;
getNewItem()
privateBottomNavigationItemViewgetNewItem(){
BottomNavigationItemViewitem=sItemPool.acquire();
if(item==null){
item=newBottomNavigationItemView(getContext());
}
returnitem;
}
getNewItem()类似于Message.obtain()的机制。
接着看buildMenuView(),方法最后面,调用了BottomNavigationItemView.initialize(),然后调用addView(child)来添加子item视图。
@Override
publicvoidinitialize(MenuItemImplitemData,intmenuType){
mItemData=itemData;
setCheckable(itemData.isCheckable());
setChecked(itemData.isChecked());
setEnabled(itemData.isEnabled());
setIcon(itemData.getIcon());
setTitle(itemData.getTitle());
setId(itemData.getItemId());
}
看一下BottomNavigationItemView的构造函数
publicBottomNavigationItemView(@NonNullContextcontext){
this(context,null);
}
publicBottomNavigationItemView(@NonNullContextcontext,AttributeSetattrs){
this(context,attrs,0);
}
publicBottomNavigationItemView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
finalResourcesres=getResources();
intinactiveLabelSize=
res.getDimensionPixelSize(R.dimen.design_bottom_navigation_text_size);
intactiveLabelSize=res.getDimensionPixelSize(
R.dimen.design_bottom_navigation_active_text_size);
mDefaultMargin=res.getDimensionPixelSize(R.dimen.design_bottom_navigation_margin);
mShiftAmount=inactiveLabelSize-activeLabelSize;
mScaleUpFactor=1f*activeLabelSize/inactiveLabelSize;
mScaleDownFactor=1f*inactiveLabelSize/activeLabelSize;
//注意下面的代码
LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item,this,true);
setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
mIcon=(ImageView)findViewById(R.id.icon);
mSmallLabel=(TextView)findViewById(R.id.smallLabel);
mLargeLabel=(TextView)findViewById(R.id.largeLabel);
}
代码中映射了一个布局文件
design_bottom_navigation_item.xml
android="
android:
id="@+id/icon"
android:
layout_width="24dp"
android:
layout_height="24dp"
android:
layout_gravity="center_horizontal"
android:
layout_marginTop="@dimen/design_bottom_navigation_margin"
android:
layout_marginBottom="@dimen/design_bottom_navigation_margin"
android:
duplicateParentState="true"/>
android:
layout_width="wrap_content"
android:
layout_height="wrap_content"
android:
layout_marginBottom="@dimen/design_bottom_navigation_margin"
android:
layout_gravity="bottom|center_horizontal"
android:
duplicateParentState="true">
android:
id="@+id/smallLabel"
android:
layout_width="wrap_content"
android:
layout_height="wrap_content"
android:
textSize="@dimen/design_bottom_navigation_text_size"
android:
duplicateParentState="true"/>
android:
id="@+id/largeLabel"
android:
layout_width="wrap_content"
android:
layout_height="wrap_content"
android:
visibility="sible"
android:
textSize="@dimen/design_bottom_navigation_active_text_size"
android:
duplicateParentState="true"/>
该布局文件由系统提供,包括一个ImageView和两个TextView。
当菜单项大于3个,切换item时,被选中的item会将largeLabel显示,将smallLabel隐藏。
然后改变ImageView和TextView的margin值达到动画效果。
在BottomNavigationMenuView的构造函数中对mAnimationHelper进行了实例化
privatefinalBottomNavigationAnimationHelperBasemAnimationHelper;
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.ICE_CREAM_SANDWICH){
mAnimationHelper=newBottomNavigationAnimationHelperIcs();
}else{
mAnimationHelper=newBottomNavigationAnimationHelperBase();
}
当版本小于14(Android4.0)时,是没有动画效果的。
classBottomNavigationAnimationHelperBase{
voidbeginDelayedTransition(ViewGroupview){
//Donothing.
}
}
Andr