自定义 BottomNavigationView

做个微信小程序的朋友都知道,要定义一个底部导航按钮非常简单,只需要配置一份json文件就行了,就会自动生成底部Tab.
Android在以前做法比较麻烦,RadioButton等自定义,要处理各种选中和默认的状态,后Android Material Design推出了BottomNavigationView,使得底部导航控件变得简单。本篇内容将说明BottomNavigationView的基本用法,和通过json文件配置自定义的BottomNavigationView。

  • 在布局文件中引入
  • <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/nav_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:background="?android:attr/windowBackground"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:menu="@menu/bottom_nav_menu" />
    

    底部要显示的内容都在menu里面bottom_nav_menu,

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
            android:id="@+id/navigation_home"
            android:icon="@drawable/ic_home_black_24dp"
            android:title="@string/title_home" />
            android:id="@+id/navigation_dashboard"
            android:icon="@drawable/ic_dashboard_black_24dp"
            android:title="@string/title_dashboard" />
            android:id="@+id/navigation_notifications"
            android:icon="@drawable/ic_notifications_black_24dp"
            android:title="@string/title_notifications" />
            android:id="@+id/home"
            android:icon="@drawable/ic_home_black_24dp"
            android:title="HomeActivity" />
    </menu>
    

    menu里面有title显示的文字,icon图标。

    在Activity中通过监听item的选中时间来实现导航。

      BottomNavigationView navView = findViewById(R.id.nav_view);
            final NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
            navView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    navController.navigate(item.getItemId(), null);
                    return true;
    

    基本用法很简单,不进行深入讲解,下面来实现自动的BottomNavigationView,

  • 配置json文件;
  • "activeColor": "#333333", "inActiveColor": "#666666", "selectTab": 0, "tabs": [ "size": 24, "enable": true, "index": 0, "pageUrl": "main/tabs/home", "title": "首页" "size": 24, "enable": true, "index": 1, "pageUrl": "main/tabs/dash", "title": "沙发" "size": 40, "enable": true, "index": 2, "tintColor": "#ff678f", "pageUrl": "main/tabs/notification", "title": "" "size": 24, "enable": true, "index": 3, "pageUrl": "main/tabs/find", "title": "发现" "size": 24, "enable": true, "index": 4, "pageUrl": "main/tabs/my", "title": "我的"

    创建一个json解析的实体类Destination

    package com.shitu.app.model;
    public class Destination {
        public String pageUrl;
        public int id;
        public boolean needLogin;
        public boolean asStarter;
        public boolean isFragment;
        public String className;
        @Override
        public String toString() {
            return "Destination{" +
                    "isFragment=" + isFragment +
                    ", asStarter=" + asStarter +
                    ", needLogin=" + needLogin +
                    ", pageUrl='" + pageUrl + '\'' +
                    ", clzName='" + className + '\'' +
                    ", id=" + id +
    

    自定义BottomNavigationView,新建一个AppBottomBar类,继承BottomNavigationView

    import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MenuItem; import com.google.android.material.bottomnavigation.BottomNavigationItemView; import com.google.android.material.bottomnavigation.BottomNavigationMenuView; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.LabelVisibilityMode; import com.shitu.app.R; import com.shitu.app.model.BottomBar; import com.shitu.app.model.Destination; import com.shitu.app.util.AppConfig; import java.util.List; public class AppBottomBar extends BottomNavigationView { private static int[] sIcons = new int[]{R.drawable.icon_tab_home, R.drawable.icon_tab_sofa, R.drawable.icon_tab_publish, R.drawable.icon_tab_find, R.drawable.icon_tab_mine}; private BottomBar config; public AppBottomBar(Context context) { this(context, null); public AppBottomBar(Context context, AttributeSet attrs) { this(context, attrs, 0); @SuppressLint("RestrictedApi") public AppBottomBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); config = AppConfig.getBottomBarConfig(); int[][] state = new int[2][]; state[0] = new int[]{android.R.attr.state_selected}; state[1] = new int[]{}; int[] colors = new int[]{Color.parseColor(config.activeColor), Color.parseColor(config.inActiveColor)}; ColorStateList stateList = new ColorStateList(state, colors); setItemTextColor(stateList); setItemIconTintList(stateList); //LABEL_VISIBILITY_LABELED:设置按钮的文本为一直显示模式 //LABEL_VISIBILITY_AUTO:当按钮个数小于三个时一直显示,或者当按钮个数大于3个且小于5个时,被选中的那个按钮文本才会显示 //LABEL_VISIBILITY_SELECTED:只有被选中的那个按钮的文本才会显示 //LABEL_VISIBILITY_UNLABELED:所有的按钮文本都不显示 setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED); List<BottomBar.Tab> tabs = config.tabs; for (BottomBar.Tab tab : tabs) { if (!tab.enable) { continue; int itemId = getItemId(tab.pageUrl); if (itemId < 0) { continue; MenuItem menuItem = getMenu().add(0, itemId, tab.index, tab.title); menuItem.setIcon(sIcons[tab.index]); //此处给按钮icon设置大小 int index = 0; for (BottomBar.Tab tab : config.tabs) { if (!tab.enable) { continue; int itemId = getItemId(tab.pageUrl); if (itemId < 0) { continue; int iconSize = dp2Px(tab.size); BottomNavigationMenuView menuView = (BottomNavigationMenuView) getChildAt(0); BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(index); itemView.setIconSize(iconSize); if (TextUtils.isEmpty(tab.title)) { int tintColor = TextUtils.isEmpty(tab.tintColor) ? Color.parseColor("#ff678f") : Color.parseColor(tab.tintColor); itemView.setIconTintList(ColorStateList.valueOf(tintColor)); //禁止掉点按时 上下浮动的效果 itemView.setShifting(false); * 如果想要禁止掉所有按钮的点击浮动效果。 * 那么还需要给选中和未选中的按钮配置一样大小的字号。 * 在MainActivity布局的AppBottomBar标签增加如下配置, * @style/active,@style/inActive 在style.xml中 * app:itemTextAppearanceActive="@style/active" * app:itemTextAppearanceInactive="@style/inActive" index++; //底部导航栏默认选中项 if (config.selectTab != 0) { BottomBar.Tab selectTab = config.tabs.get(config.selectTab); if (selectTab.enable) { int itemId = getItemId(selectTab.pageUrl); //这里需要延迟一下 再定位到默认选中的tab //因为 咱们需要等待内容区域,也就NavGraphBuilder解析数据并初始化完成, //否则会出现 底部按钮切换过去了,但内容区域还没切换过去 post(() -> setSelectedItemId(itemId)); private int dp2Px(int dpValue) { DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); return (int) (metrics.density * dpValue + 0.5f); private int getItemId(String pageUrl) { Destination destination = AppConfig.getDestConfig().get(pageUrl); if (destination == null) return -1; return destination.id;

    代码简单,没啥可说的,不懂的留言,哈哈!
    将布局文件中的BottomNavigationView换成AppBottomBar

     <com.shitu.app.ui.view.AppBottomBar
            android:id="@+id/nav_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:background="?android:attr/windowBackground"
            app:layout_constraintBottom_toBottomOf="parent"