上午记录了TabLayout的使用,简单实现了一个顶部可滑动的导航效果,突然想到Material Design的另一个控件BottomNavigationView,可以实现类似淘宝、微信、QQ、京东的底部导航栏的效果,下面就来介绍一下使用BottomNavigationView来实现底部导航栏的效果。
使用该控件同样需要添加Material Design的依赖:(运行环境是在Android Studio 3.0)
implementation 'com.android.support:design:28.0.0-alpha1'
因为BottomNavigationView控件是通过app:menu属性,使用Menu的形式为底部导航栏指定元素的,所以第一步就要新建一个菜单xml文件,在menu文件夹下新建bottom_navigation_view.xml,布局内容如下:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/item_home"
android:icon="@drawable/ic_home_grey"
android:title="首页"/>
<item android:id="@+id/item_music"
android:icon="@drawable/ic_music_grey"
android:title="音乐"/>
<item android:id="@+id/item_find"
android:icon="@drawable/ic_find_grey"
android:title="发现"/>
</menu>
BottomNavigationView一般也是和ViewPager+Fragment搭配使用,所以第二步就写下布局文件,
activity_bottom_navigation_view.xml布局内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BottomNavigationViewActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#6b6b6b"/>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/bottom_navigation_view">
</android.support.design.widget.BottomNavigationView>
</LinearLayout>
创建BottonNaviFragment继承自Fragment,其布局文件fragment_bottom_navi.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:textColor="#000000"
android:id="@+id/tv_content"/>
</LinearLayout>
BottonNaviFragment.java文件如下:
@SuppressLint("ValidFragment")
public class BottonNaviFragment extends Fragment {
private TextView textView;
private String title;
public BottonNaviFragment(String title) {
this.title = title;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_bottom_navi,container,false);
textView=view.findViewById(R.id.tv_content);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
textView.setText(title);
}
}
创建Fragment适配器文件FragmentAdapter.java文件如下:
public class FragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> list; //存放ViewPager中要填充的Fragment
public FragmentAdapter(FragmentManager fm,List<Fragment> list) {
super(fm);
this.list=list;
}
@Override
public Fragment getItem(int i) {
return list.get(i);
}
@Override
public int getCount() {
return list.size();
}
}
BottomNavigationViewActivity.java文件如下:
public class BottomNavigationViewActivity extends AppCompatActivity {
private ViewPager viewPager;
private BottomNavigationView bottomNavigationView;
private MenuItem menuItem; //菜单子项
private List<Fragment> list;
private FragmentAdapter fragmentAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_navigation_view);
initView();
initData();
}
private void initView() {
viewPager=findViewById(R.id.viewpager);
bottomNavigationView=findViewById(R.id.bottom_navigation_view);
//viewPager滑动监听
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if(menuItem!=null){
menuItem.setChecked(false);
}else{
bottomNavigationView.getMenu().getItem(0).setChecked(false);
}
menuItem=bottomNavigationView.getMenu().getItem(position);
menuItem.setChecked(true);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
//bottmNavigationView菜单选择监听
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()){
case R.id.item_home:
viewPager.setCurrentItem(0);
break;
case R.id.item_music:
viewPager.setCurrentItem(1);
break;
case R.id.item_find:
viewPager.setCurrentItem(2);
break;
}
return false;
}
});
}
private void initData() {
list=new ArrayList<>();
list.add(new BottonNaviFragment("首页"));
list.add(new BottonNaviFragment("音乐"));
list.add(new BottonNaviFragment("发现"));
fragmentAdapter=new FragmentAdapter(getSupportFragmentManager(),null,list);
viewPager.setAdapter(fragmentAdapter);
}
}
到此运行下项目,效果如图:
默认元素选中时图标、文字的颜色为@color/colorPrimary,如果我们想改变导航栏中图标、文字在选中和未选中时的颜色,可以通过BottomNavigationView控件的两个属性去实现,分别是
app:itemTextColor=""
app:itemIconTint=""
为了方便效果展示,在这里我们设定图标、文字在选中时颜色为红色,未选中时为黑色,涉及到颜色选择,需要在color文件夹下新建一个颜色选择器bottomnavigation_select.xml文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#ff2200" android:state_checked="true"/>
<item android:color="#000000"/>
</selector>
然后在activity_bottom_navigation_view.xml中的BottomNavigationView控件下,增加属性:
app:itemTextColor="@color/bottomnavigation_select"
app:itemIconTint="@color/bottomnavigation_select"
运行一下,效果如图:
以上是导航栏只有3个元素时效果,下面将元素增加到4个,运行效果如图:
从图中可以发现,当导航栏中元素增加到4个时,效果就不一样了,只有当元素选中以及滑动到对应元素时,文字才会出现,未选中时,文字是隐藏的。 这是因为官方的BottomNavigationView默认有个放大的ShiftingMode效果,但是尚未支持代码层级的切换。在3个元素及以下时是默认关闭的,而到了4个及以上时就会开启ShiftingMode效果,并且没有任何属性和方法去修改ShiftingMode,此时我们只能通过反射来修改:
新建一个BottomNavigationView的帮助者类BottomNavigationViewHelper.java,代码如下:
import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;
import android.util.Log;
import java.lang.reflect.Field;
/**
* 新建一个BottomNavigationview帮助者类,
* 通过反射来修改ShiftingMode
*/
public class BottomNavigationViewHelper {
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);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
}
然后在 BottomNavigationViewActivity.java中调用BottomNavigationViewHelper的静态方法disableShiftMode()即可。
bottomNavigationView=findViewById(R.id.bottom_navigation_view);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
当你的build.gradle中 依赖库 'com.android.support:appcompat-v7:X.0.0-rc02' 中X小于28时,以上代码是没有问题的,当X等于28时(我使用的是Android Studio 3.0,依赖包是implementation 'com.android.support:appcompat-v7:28.0.0-rc02'),item.setShiftingMode(false)就会报Cannot resolve method 'setShiftingMode(Boolean)'的错误,借鉴了
https://stackoverflow.com/questions/51342200/cannot-resolve-method-setshiftingmodeboolean-in-bottomnavigationview的解决方式,将BottomNavigationViewHelper.java代码修改如下:
public class BottomNavigationViewHelper {
@SuppressLint("RestrictedApi")
public static void removeNavigationShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
menuView.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED);
menuView.buildMenuView();
}
}
同样的,然后在 BottomNavigationViewActivity.java中调用BottomNavigationViewHelper的静态方法removeNavigationShiftMode()
bottomNavigationView=findViewById(R.id.bottom_navigation_view);
BottomNavigationViewHelper.removeNavigationShiftMode(bottomNavigationView);
一番修改之后,再运行下项目,看下效果:
此时,4个元素的效果和3个元素的效果就一样啦。
最后关于BottomNavigationView控件作点补充:
1、如果要设置底部导航栏的背景颜色,可以通过BottomNavigationView的属性app:itemBackground来设置,默认是当前主题的背景色,白色or黑色。
2、官方建议导航栏元素item个数为3-5个 ,最多5个,如果设置6个会直接报错。设置2个则不会报错,但是如果是2个的话不建议使用该控件。
3、如果想实现元素是不带文字的图标,可以不设置菜单的title值,例如,不设置“发现”的元素文字,运行后效果如下方:
好啦,关于BottomNavigationView实现底部导航栏的使用就介绍到这里啦。