关于BottomNavigationView的使用姿势都在这里了 | 您所在的位置:网站首页 › 底部导航是什么 › 关于BottomNavigationView的使用姿势都在这里了 |
一,基本用法
1,首先需要添加依赖: implementation 'com.google.android.material:material:1.1.0' 2,布局文件中引入: 3,常用属性: app:itemTextColor 文字的颜色,可以通过selector来控制选中和未选中的颜色 app:itemIconTint 图标的颜色,可以通过selector来控制选中和未选中的颜色 app:itemIconSize 图标大小,默认24dp app:iteamBackground 背景颜色,默认是主题的颜色 app:itemRippleColor 点击后的水波纹颜色 app:itemTextAppearanceActive 设置选中时文字样式 app:itemTextAppearanceInactive 设置默认的文字样式 app:itemHorizontalTranslationEnabled 在label visibility 模式为selected时item水平方向移动 app:elevation 控制控件顶部的阴影 app:labelVisibilityMode 文字的显示模式 app:menu 指定菜单xml文件(文字和图片都写在这个里面) 4,menu文件: 在每个item中设置对应的icon和title即可。这里的icon可以是一个drawable,也可以是包含不同状态对应不同图片的selector。 设置了menu后一个基本的底部菜单栏就有了。 5,常用事件 主要用两个事件OnNavigationItemSelectedListener和OnNavigationItemReselectedListener nav_view.setOnNavigationItemSelectedListener( BottomNavigationView.OnNavigationItemSelectedListener { when (it.itemId) { R.id.navigation_item1 -> { Log.e("bottomMenuView:", "home") return@OnNavigationItemSelectedListener true } R.id.navigation_item2 -> { Log.e("bottomMenuView:", "dashboard") return@OnNavigationItemSelectedListener true } R.id.navigation_item3 -> { Log.e("bottomMenuView:", "notification") return@OnNavigationItemSelectedListener true } } false }) nav_view.setOnNavigationItemReselectedListener( BottomNavigationView.OnNavigationItemReselectedListener { Log.e("bottomMenuView:", it.itemId.toString()) }) 两个事件的用法是一样的,区别在于:OnNavigationItemSelectedListener在item由未选中到选中状态时触发,而OnNavigationItemReselectedListener在item处于选中状态再次点击时触发。 6,最大Item数量 BottomNavigationView对显示的item数量做了显示,最多5个,超过就会抛出异常,源码如下: public final class BottomNavigationMenu extends MenuBuilder { public static final int MAX_ITEM_COUNT = 5; // ... @Override protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) { if (size() + 1 > MAX_ITEM_COUNT) { throw new IllegalArgumentException( "Maximum number of items supported by BottomNavigationView is " + MAX_ITEM_COUNT + ". Limit can be checked with BottomNavigationView#getMaxItemCount()"); } // ... return item; } } google这么做的原因大概是一般不会有超过5个的需求,而且超过5个以后就会显得很拥挤,UI效果比较差。 二,配合fragment 单纯使用BottomNavigationView并没有什么卵用,一般都是配合fragment来使用。配合fragment使用时有三种方式: 1,FrameLayout + FragmentTransaction 比较古老的一种方式通过getSupportFragmentManager().beginTransaction()获取到FragmentTransaction ,然后通过FragmentTransaction的add,show,hide等方法来控制fragment的显示,这种方式比较繁琐就不赘述了。 2,ViewPager ViewPager一种比较流行的方式,当然你也可以用ViewPager2,用法差不多。需要在布局文件中添加ViewPager。 2.1,设置ViewPager的adapter,如下: mFragments.add(Fragment1.newInstance()) mFragments.add(Fragment2.newInstance()) mFragments.add(Fragment3.newInstance()) val adapter = object : FragmentStatePagerAdapter( supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT ) { override fun getItem(position: Int): Fragment { return mFragments[position] } override fun getCount(): Int { return mFragments.size } } viewPager.adapter = adapter 然后需要将ViewPager和BottomNavigationView绑定。 2.2,ViewPager绑定BottomNavigationView 就是在ViewPager切换时更改BottomNavigationView选中项 // 添加viewpager切换监听 viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { // ... override fun onPageSelected(position: Int) { when (position) { 0 -> { navigation.selectedItemId = R.id.navigation_item1 } 1 -> { navigation.selectedItemId = R.id.navigation_item2 } 2 -> { navigation.selectedItemId = R.id.navigation_item3 } } } }) 2.3,BottomNavigationView绑定ViewPager 同样的,需要在BottomNavigationView选中项改变时更改ViewPager: navigation.setOnNavigationItemSelectedListener( BottomNavigationView.OnNavigationItemSelectedListener { item -> when (item.itemId) { R.id.navigation_item1 -> { viewPager.currentItem = 0 return@OnNavigationItemSelectedListener true } R.id.navigation_item2 -> { viewPager.currentItem = 1 return@OnNavigationItemSelectedListener true } R.id.navigation_item3 -> { viewPager.currentItem = 2 return@OnNavigationItemSelectedListener true } } false })3,配合navigation 这种方式是Google官方目前主推的方式,需要你对navigation有所了解。 1,布局文件如下: 2,navigation文件 在navigation中指定了对应的fragment 3,Activity中 接下来的使用就很简单了,调用Activity的扩展函数findNavController,根据布局文件中的fragment标签的id获取NavController,将NavController和BottomNavigationView绑定即可,如下: val navView: BottomNavigationView = findViewById(R.id.nav_view) val navController = findNavController(R.id.nav_host_fragment) navView.setupWithNavController(navController) 效果如下: 还在github上查找怎么往BottomNavigationView上添加badge吗?BottomNavigationView默认提供了badge。 1,基本使用 在BottomNavigationView上添加badge很简单,它提供了如下操作badge的方法: getBadge(int menuItemId) 获取badge getOrCreateBadge(int menuItemId) 获取或创建badge removeBadge(int menuItemId) 移除badge 因此添加一个badge只需要如下代码: val navView: BottomNavigationView = findViewById(R.id.nav_view) val badge = navView.getOrCreateBadge(R.id.navigation_dashboard) 效果如下: 纳尼?怎么只有一个红点,胸弟别鸡动,还没设置数量 badge.number = 20 添加数量后效果如下: 2,常用属性 getBadge和getOrCreateBadge方法返回的都是BadgeDrawable,BadgeDrawable常用的属性/方法如下: backgroundColor 设置背景色 badgeGravity 设置Badge的显示位置,有四种可先:TOP_START,TOP_END,BOTTOM_START,BOTTOM_END,分别对应左上角,右上角,左下角和右下角。 badgeTextColor 设置文字颜色 maxCharacterCount 最多显示几位数字,比如该项设置了3,number设置为108,则显示99+,如下图所示: 3,注意事项 需要你Application的Theme继承自Theme.MaterialComponents,如下所示: @color/colorPrimary @color/colorPrimaryDark @color/colorAccent 4,扩展 同样是位于com.google.android.material包中的TabLayout也可以用同样的方式添加badge tabLayout.getTabAt(0).orCreateBadge.apply { number = 10 backgroundColor = Color.RED } 四,侧边导航有人可能会有这样的需求,在横屏状态时想让导航栏显示在左侧而不是底部,那该如何实现呢?使用BottomNavigationView肯定是不行的,因为它的名字是Bottom,只能显示在底部。不过好在material:1.4.0版本开始提供了一个BottomNavigationView的兄弟组件:NavigationRailView。 NavigationRailView的使用也非常简单和BottomNavigationView基本一样。比如我们想要实现如下在横屏时导航栏在左侧的效果: 只需要在layout-land中添加一个同样的布局,然后把BottomNavigationView换成NavigationRailView,并调整布局到左侧即可。 PS:屏幕旋转时要注意导航栏状态的保存 参考官方:https://developer.android.google.cn/guide/topics/large-screens/navigation-for-responsive-uis 五,常用需求 1,动态显示/隐藏MenuItem 有些时候需要根据条件来控制menuItem是否显示,有两种方式可以实现: 1.1, remove val navView: BottomNavigationView = findViewById(R.id.nav_view) navView.menu.removeItem(R.id.navigation_spacing) 这种方式是直接把这个item删除掉了,是一个不可逆的过程,也就是说删除后没法再显示出来 1.2, setVisible // 显示 nav_view.menu.findItem(R.id.navigation_test).isVisible = true // 隐藏 nav_view.menu.findItem(R.id.navigation_test).isVisible = false 效果如下: 2,更改字体颜色 创建selector: 设置app:itemTextColor属性: 效果如下: 3,修改字体大小 字体大小分为选中的大小和未选中的大小,他们的默认值分别是14sp/12sp,可以通过覆盖原来的字体大小来改变字体大小。 14sp 14sp 修改前后的效果: 4,自定义选中图标颜色 图标的颜色是通过着色实现的,如果我们的图标不是纯色就需要特殊处理了。 比如如下图标: 可以通过selector来定义选中和未选中的状态,并设置给menu item的icon 然后在activity中设置itemIconTintList为null nav_view.itemIconTintList = null 效果如下: 5,labelVisibilityMode 在前面基本属性中已经提到,文字的显示模式有四种: auto 这种模式就是item数量在三个及以下全部显示label,三个以上只显示选中item的label,效果如下: selected 该模式下,不管item数量是多少都只显示选中的item的label labeled 该模式下,不管item数量是多少item的label都显示 unlabeled 该模式下,item的label始终不显示 label的显示模式可以在布局文件中通过labelVisibilityMode设置,也可以在java代码中通过setLabelVisibilityMode设置 6,切换时的动画效果? 很多人说切换时会有动画效果,如下: 其实这并不是什么动画效果,只是因为选中时文字的字体变大了(实际上是两个字体大小不一样的TextView切换显示状态),把图标撑起来了,再加上点击时的Ripple效果,感觉就是一个高大上的动效。要去掉这个效果只需要将字体选中和默认的大小改成一致即可,效果如下: 7,图标文字间距 7.1 调整图标到顶部的距离 如果想调整图标和文字间的距离,改怎么办呢?查了一些资料大部分都是通过添加dimen覆盖默认的design_bottom_navigation_margin来实现。 4dp 该值默认是把8dp,把它调小了,发现图标和文字的距离变大了,这是怎么回事?其实这个距离并不是图标和文字的间距,而是图标距离顶部和底部的Margin值,调小后到顶部的距离也变小了,就显得图标和文字的距离变大了。 如果你有显示badge的需求,那这种方式就出问题了,因为badge是依附于图标的,图标上移,badge也会跟着上移,可以就显示不全了 7.2 调整文字到底部的距离 那么如果我想调整文字到底部的距离呢?这就需要了解一下每个Item的布局文件design_bottom_navigation_item.xml,其源代码(部分代码省略)如下: 可见如果想修改文字到底部的距离可以改动其父容器BaselineLayout的bottomMargin属性,或调用父容器的scrollBy()使内部的TextView向下偏移,当然此时就需要用到反射。 public static void hookBnv(BottomNavigationView menu) throws Exception{ Class bottomNavigationViewClass = BottomNavigationView.class; Field menuViewField = bottomNavigationViewClass.getDeclaredField("menuView"); menuViewField.setAccessible(true); Object menuView = menuViewField.get(menu); Class bottomNavigationMenuViewClass = BottomNavigationMenuView.class; Field buttonsField = bottomNavigationMenuViewClass.getDeclaredField("buttons"); buttonsField.setAccessible(true); Object[] buttons = (Object[]) buttonsField.get(menuView); for (Object button : buttons) { Class bottomNavigationItemViewClass = BottomNavigationItemView.class; Field smallLabelField = bottomNavigationItemViewClass.getDeclaredField("smallLabel"); smallLabelField.setAccessible(true); TextView smallLabel = (TextView) smallLabelField.get(button); // 方式一: // ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) ((ViewGroup) smallLabel.getParent()).getLayoutParams(); // layoutParams.bottomMargin = -15; // ((ViewGroup) smallLabel.getParent()).setLayoutParams(layoutParams); // 方式二: ((ViewGroup) smallLabel.getParent()).scrollBy(0,-15); } } 效果如下: 8,修改控件高度 BottomNavigationView的默认高度是56dp,如果遇到操蛋的需求非要改它的话就覆盖一下design_bottom_navigation_height吧,如下: 84dp 六,结语 关于BottomNavigationView可能用到的知识点就这些了,如果你还有还有什么奇葩的需求,那就去**“look the fuck resource code”** 吧 |
CopyRight 2018-2019 实验室设备网 版权所有 |