天天看点

Android底部导航栏——bottomnavigation结合viewpager的实现

前言

在谷歌官方发布BottomNavigationView控件之前我们可以自己组合控件实现,比如LinearLayout + TextView(使用android:drawableTop属性+selector状态切换)、RadioGroup + RadioButton等等组合控件的方法自定义实现复杂效果。除了第三方外,现在我们多了一个选择,就是使用bottomnavigation结合viewpager的实现。后期会有文章介绍前面的几种实现方式,对比下来这种代码量最少,而且特别清晰。

一 使用LinearLayout + TextView实现了底部导航栏的效果

学习一种新的技术首先要看在gradle中的配置(菇凉能力有限这篇文章只讲解这个控件是怎么用的,感兴趣的小伙伴可以去看源代码):

1.1 build.gradle中

compile'com.android.support:design:25.0.1'
 compile'com.android.support:support-v4:25.0.1'
           

1.2 xml文件中

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context="com.bottomnavigationview.MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottom_navigation" />

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        app:itemIconTint="@drawable/bottom_selector"
        app:itemTextColor="@drawable/bottom_selector"
        app:menu="@menu/bottom_menu" />

    <View
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_above="@id/bottom_navigation"
        android:background="@drawable/bottomshadow" />
</RelativeLayout>

           

这里简单说一下

android.support.design.widget.BottomNavigationView

中的一些属性:

  1. app:itemIconTint="@drawable/bottom_selector" :这个是底部栏图片颜色变化的selector
  2. app:itemTextColor="@drawable/bottom_selector":这个是底部栏文字颜色变化的selector
  3. app:menu="@menu/bottom_menu":这个是底部栏模块item的定义,包括文字和图片

1.3 在res下新建menu文件夹,新建一个menu菜单 ,代码如下:

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

    <item
        android:id="@+id/item_news"
        android:icon="@mipmap/icon_navigation_home_01"
        android:title="首页" />
    <item
        android:id="@+id/item_lib"
        android:icon="@mipmap/icon_navigation_shop_01"
        android:title="搜索" />
    <item
        android:id="@+id/item_find"
        android:icon="@mipmap/icon_navigation_ucenter_01"
        android:title="我的" />
    <item
        android:id="@+id/item_more"
        android:icon="@mipmap/icon_navigation_home_01"
        android:title="主页" />
</menu>
           

1.4 在drawable下新建.xml文件 颜色和文字变化的颜色一样selector,代码如下

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/p_select_color"         android:state_checked="true" />
    <item android:color="@color/p_99_color" android:state_checked="false" />
</selector>
           

1.5 MainActivity中的主要代码:

package com.bottomnavigationview;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    private ViewPager viewPager;
    private MenuItem menuItem;
    private BottomNavigationView bottomNavigationView;

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

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottom_navigation);
        //默认 >3 的选中效果会影响ViewPager的滑动切换时的效果,故利用反射去掉
 BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        switch (item.getItemId()) {
                            case R.id.item_news:
                                viewPager.setCurrentItem(0);
                                break;
                            case R.id.item_lib:
                                viewPager.setCurrentItem(1);
                                break;
                            case R.id.item_find:
                                viewPager.setCurrentItem(2);
                                break;
                            case R.id.item_more:
                                viewPager.setCurrentItem(3);
                                break;
                        }
                        return false;
                    }
                });

        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) {
            }
        });

        //禁止ViewPager滑动
//        viewPager.setOnTouchListener(new View.OnTouchListener() {
//            @Override
//            public boolean onTouch(View v, MotionEvent event) {
//                return true;
//            }
//        });

        setupViewPager(viewPager);
    }

    private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

        adapter.addFragment(BaseFragment.newInstance("首页"));
        adapter.addFragment(BaseFragment.newInstance("搜索"));
        adapter.addFragment(BaseFragment.newInstance("我的"));
        adapter.addFragment(BaseFragment.newInstance("主页"));
        viewPager.setAdapter(adapter);
    }

}

           

注意点:

官方的BottomNavigationView用起来虽然简单,但是有一个很坑的问题,就是它内部在item>4的时候出现动画的效果。如下:

Android底部导航栏——bottomnavigation结合viewpager的实现

在平时的开发中这种效果也是特别崩溃的,因为官方没有默认的方法取消这种效果,这时候需要我们用到反射。

创建一个Helper类

package com.bottomnavigationview;

import android.annotation.TargetApi;
import android.os.Build;
import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;

import java.lang.reflect.Field;

/**
 * Created by 凉菇凉 on 2017/8/2.
 */
public class BottomNavigationViewHelper {

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static 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 (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

           

使用的时候

BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
           

1.6 其实避了这个坑是特别简单的,viewpager就不在这里详细说了。来上一张效果动图。

Android底部导航栏——bottomnavigation结合viewpager的实现
补充 :

BaseFragment中的代码

public class BaseFragment  extends Fragment {

    private static final String EXTRA_CONTENT = "content";


    public static BaseFragment newInstance(String content){
        Bundle arguments = new Bundle();
        arguments.putString(EXTRA_CONTENT, content);
        BaseFragment tabContentFragment = new BaseFragment();
        tabContentFragment.setArguments(arguments);
        return tabContentFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.fragment_tab_content, null);
        ((TextView)contentView.findViewById(R.id.tv_content)).setText(getArguments().getString(EXTRA_CONTENT));
        return contentView;
    }
}