BottomNavigationView+Fragment实现底部导航栏

导入依赖库

implementation 'com.android.support:design:27.1.1'

编辑布局文件

<?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">

    <FrameLayout
        android:id="@+id/fragment_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

    </FrameLayout>

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:itemIconTint="@color/tab_text_color_checked_selector"
        app:itemBackground="@android:color/transparent"
        app:itemTextColor="@color/tab_text_color_checked_selector"
        app:menu="@menu/tabs_menu">

    </android.support.design.widget.BottomNavigationView>

</LinearLayout>

属性说明:

  • itemIconTint:设置tab图片的颜色,像控制文字颜色一样使用selector来控制
  • itemBackground:设置tab的背景,可以用来消除点击时的涟漪效果
  • itemTextColor:设置tab文字的颜色
  • menu:设置tab菜单

创建tabs_menu

在项目的res文件下创建menu文件夹,再创建tabs_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/tab_home"
        android:icon="@drawable/ic_home_common"
        android:title="首页" />

    <item
        android:id="@+id/tab_classification"
        android:icon="@drawable/ic_classification_common"
        android:title="分类" />

    <item
        android:id="@+id/tab_case"
        android:icon="@drawable/ic_case_common"
        android:title="案例" />

    <item
        android:id="@+id/tab_setting"
        android:icon="@drawable/ic_setting_common"
        android:title="设置" />

</menu>

activity代码

package com.matrix.navigation;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;

import com.matrix.navigation.fragment.CaseFragment;
import com.matrix.navigation.fragment.ClassificationFragment;
import com.matrix.navigation.fragment.HomeFragment;
import com.matrix.navigation.fragment.SettingFragment;

import java.lang.reflect.Field;

public class BottomNavigationViewActivity extends AppCompatActivity {

    private Fragment mHomeFragment = new HomeFragment();
    private Fragment mClassificationFragment = new ClassificationFragment();
    private Fragment mCaseFragment = new CaseFragment();
    private Fragment mSettingFragment = new SettingFragment();
    private Fragment[] mFragments = new Fragment[]{mHomeFragment, mClassificationFragment,
            mCaseFragment, mSettingFragment};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation_view);

        BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                navigationItemSelected(item.getItemId());
                return true;
            }
        });

        // 手动设置第一个展示的fragment
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_content, mHomeFragment).commit();
    }

    private void navigationItemSelected(int itemId) {
        Fragment fragment = null;
        switch (itemId) {
            case R.id.tab_home:
                fragment = mFragments[0];
                break;
            case R.id.tab_classification:
                fragment = mFragments[1];
                break;
            case R.id.tab_case:
                fragment = mFragments[2];
                break;
            case R.id.tab_setting:
                fragment = mFragments[3];
                break;
        }

        if (fragment != null) {
            getSupportFragmentManager().beginTransaction().replace(R.id.fragment_content, fragment).commit();
        }
    }

}

来跑一下,看下效果

一脸懵,这是啥效果,完全不是我们想要的?为什么会这样呢?看源码!!!(ps:主要需要关注三个类BottomNavigationView.java、BottomNavigationMenuView.java、BottomNavigationItemView.java)在源码的BottomNavigationMenuView.java中有这么一段代码

public void buildMenuView() {
        removeAllViews();
        if (mButtons != null) {
            for (BottomNavigationItemView item : mButtons) {
                mItemPool.release(item);
            }
        }
        if (mMenu.size() == 0) {
            mSelectedItemId = 0;
            mSelectedItemPosition = 0;
            mButtons = null;
            return;
        }
        mButtons = new BottomNavigationItemView[mMenu.size()];
        mShiftingMode = mMenu.size() > 3;
        for (int i = 0; i < mMenu.size(); i++) {
            mPresenter.setUpdateSuspended(true);
            mMenu.getItem(i).setCheckable(true);
            mPresenter.setUpdateSuspended(false);
            BottomNavigationItemView child = getNewItem();
            mButtons[i] = child;
            child.setIconTintList(mItemIconTint);
            child.setTextColor(mItemTextColor);
            child.setItemBackground(mItemBackgroundRes);
            child.setShiftingMode(mShiftingMode);
            child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
            child.setItemPosition(i);
            child.setOnClickListener(mOnClickListener);
            addView(child);
        }
        mSelectedItemPosition = Math.min(mMenu.size() - 1, mSelectedItemPosition);
        mMenu.getItem(mSelectedItemPosition).setChecked(true);
    }

看到没,这种效果叫做ShiftingMode,当tab超过3个的话,ShiftingMode就会被设置为true,我们先将tab个数控制三个以内看看效果,删掉tab_menu.xml中的一个item。

好像达到了我们需要的效果,但是被选中的文字变大了(这个问题稍候在处理)。先处理如何在tab个数大于3个时也是这样的效果呢?很简单,我们只要控制住shiftingMode为false就行了,那怎么做到呢?通过反射机制来处理。具体代码如下:

    @SuppressLint("RestrictedApi")
    public void disableShiftMode(BottomNavigationView navigationView) {

        BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.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 itemView = (BottomNavigationItemView) menuView.getChildAt(i);
                itemView.setShiftingMode(false);
                itemView.setChecked(itemView.getItemData().isChecked());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

然后在activity中调用该方法即可,disableShiftMode(bottomNavigationView)。看下效果

的确如此,达到了我们想要的效果。接下来我们处理点击后文字变大的文字,在源码BottomNavigationItemView.java类中有这样一段代码

    public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        final Resources res = getResources();
        int inactiveLabelSize =
                res.getDimensionPixelSize(R.dimen.design_bottom_navigation_text_size);
        int activeLabelSize = 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 = findViewById(R.id.icon);
        mSmallLabel = findViewById(R.id.smallLabel);
        mLargeLabel = findViewById(R.id.largeLabel);
    }

关注其中三个值:

  • design_bottom_navigation_text_size:未选中时文字大小,默认12sp
  • design_bottom_navigation_active_text_size:选中时文字大小,默认14sp
  • design_bottom_navigation_margin:图片与文字的间距,默认8dp

所以我们可以在自己的dimens文件中将design_bottom_navigation_text_sizedesign_bottom_navigation_active_text_size设置同样大小即可。

在这里贴出源码中tab item所使用到的布局文件design_bottom_navigation_item.xml。

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2016 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <ImageView
        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.support.design.internal.BaselineLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:clipToPadding="false"
        android:paddingBottom="10dp"
        android:duplicateParentState="true">
        <TextView
            android:id="@+id/smallLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/design_bottom_navigation_text_size"
            android:singleLine="true"
            android:duplicateParentState="true" />
        <TextView
            android:id="@+id/largeLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="invisible"
            android:textSize="@dimen/design_bottom_navigation_active_text_size"
            android:singleLine="true"
            android:duplicateParentState="true" />
    </android.support.design.internal.BaselineLayout>
</merge>

在源码BottomNavigationMenuView.java中还有另外一个参数值需要注意,design_bottom_navigation_height用来设置默认的底部导航栏的高度,默认值为56dp。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值