上午记录了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,布局内容如下:
BottomNavigationView一般也是和ViewPager+Fragment搭配使用,所以第二步就写下布局文件,
activity_bottom_navigation_view.xml布局内容如下:
创建BottonNaviFragment继承自Fragment,其布局文件fragment_bottom_navi.xml文件如下:
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 Listlist; //存放ViewPager中要填充的Fragment public FragmentAdapter(FragmentManager fm,List 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 Listlist; 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文件,内容如下:
然后在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实现底部导航栏的使用就介绍到这里啦。