BottomNavigationView + ViewPager + Fragment 实现左右滑动和下方导航栏

转载请注明出处:http://blog.csdn.net/htwhtw123/article/details/78441431
比较简单的用BottomNavigationView 、 ViewPager 、 Fragment 实现下方导航栏与上方可翻页页面,页面布局和逻辑在自定义的Fragment类中自行定义。点击这里跳转源码
注意,所有项目中所有Fragment都是导入:import android.support.v4.app.Fragment;
(注意:BottomNavigationView如果是新建项目想要引入的话,可以在创建项目的时候自动生成(新建项目时,选下图的一个activity),无需再看下面的1~4步骤)
这里写图片描述
(还有,新建活动也可以自动生成带有BottomNavigationView 的Activity,如下方式去新建活动):
这里写图片描述
(可以通过如上两种方式非常快速的完成对BottomNavigationView相关代码编写,但是请不要有BottomNavigationView所需的同名文件,否则因报错而无法生成。)
下面讲解一下纯手写非自动生成的方法:

1.BottomNavigationView所需库导入:

ctrl+alt+shift+s 打开project struct->选择左边的Module(默认是app)->点击右上 Dependencies->点击最右侧“+”符号->点击Library dependency->搜索栏输入design->选择以com.android开头的一项,点击OK。现在的导入的是下面的版本:
这里写图片描述
(采用这种方法而不是去build.gradle粘代码后同步,导入的会一直是最新版本的库。还有,一般android的库都是以com.android开头的)

2.创建导航栏图标

(也可以在网上自寻找png图片,因为只会显示点中和没有点中两种颜色,挑选时只需要注意形状即可,颜色会失效)
右击drawable文件夹->New->Vector Asset->点击下面圈出来的地方就可以选择图片。->选好后OK,next,finish就可以生成图片
这里写图片描述

3.设置导航栏子项

在res文件夹下新建menu文件夹,在menu中新建文navigation.xml:右击menu->new->Menu Resource,添加导航栏子项,从前到后依次会是导航栏从左到右的子项。

下面是完整代码

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

    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home"/>

    <item
        android:id="@+id/navigation_dashboard"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="@string/title_dashboard"/>

    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_notifications"/>

    <item
        android:id="@+id/navigation_notifications2"
        android:icon="@drawable/ic_settings_black_24dp"
        android:title="@string/title_notifications"/>

</menu>

item属性讲解:
id属性是必要的,需要在点击监听时时使用;android:icon是图标,同一时间只会显示一种颜色,只有选中和未选中时的两种颜色;android:title是会在图标下显示的字,默认情况只有选中项的字才会显示。

4.在布局中添加BottomNavigationView和 ViewPager

这种情况,我喜欢用LinearLayout,上方的ViewPager占满剩余屏幕(android:layout_weight=”1”),下方BottomNavigationView 高度为wrap_content。下面是Activity的布局文件。app:menu=”@menu/navigation”将刚刚写的子项定义文件加载进来,android:background=”?android:attr/windowBackground”的效果是:当BottomNavigationView与上方内容同色时,会显示的一条分割线,如下图中部。
这里写图片描述

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_weight="1"
        android:layout_height="0dp"/>


    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/windowBackground"
        app:menu="@menu/navigation"/>

</LinearLayout>

现在BottomNavigationView已经可以成功显示在Activity中了。下面介绍ViewPager初始化。

5.为ViewPager创建适配器

因为要添加的页面是Fragment所以让MyFragment继承FragmentPagerAdapter。listFragment用来存储显示页面对象。
(注意,项目中所有Fragment都是如下导入:import android.support.v4.app.Fragment,不要导入另一种),


class MyFragAdapter extends FragmentStatePagerAdapter {
    Context context;
    List<Fragment> listFragment;

    public MyFragAdapter(FragmentManager fm, Context context, List<Fragment> listFragment) {
        super(fm);
        this.context = context;
        this.listFragment = listFragment;
    }

    @Override
    public Fragment getItem(int position) {
        return listFragment.get(position);
    }

    @Override
    public int getCount() {
        return listFragment.size();
    }

6.为ViewPager准备显示的Fragment页面

这里写一个例子。
首先是准备Fragment的布局,这里没有限制,根据需要自己写就行,为了举个Fragment中控件使用的简单例子,我加了一个按钮。下面是frag1.xml的代码:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="frag1"
        android:textSize="30sp"/>

    <Button
        android:id="@+id/bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment中的按钮"
        android:textAllCaps="false"/>
</LinearLayout>

然后下面是加载这个布局的Fragment1.java中的代码,获取context使用getActivity()方法。通过获取的view,使用view.findViewById()可以获取控件的对象。


public class Fragment1 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag1, container, false);
        Button bt = (Button) view.findViewById(R.id.bt);
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "Fragment1上的按钮被点击了", Toast.LENGTH_SHORT).show();
            }
        });
        return view;
    }
}

关于使用碎片踩过一些坑,有兴趣的朋友可以看一下:Android 碎片使用的一些坑
之后我与之上相似的又写了3个Fragment。

7.活动中的代码逻辑

终于到最重要的地方了但是依旧简单,这里将ViewPager和BottomNavigationView 关联到一起的思想就是:监听他们的点击或滑动事件,然后将对方设置成滑动到相应界面或相应子项被点下的状态。下面是代码:


public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    ViewPager viewPager;
    BottomNavigationView navigation;//底部导航栏对象
    List<Fragment> listFragment;//存储页面对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//初始化
    }

    private void initView() {
        viewPager = (ViewPager) findViewById(R.id.view_pager);
        navigation = (BottomNavigationView) findViewById(R.id.navigation);

        //向ViewPager添加各页面
        listFragment = new ArrayList<>();
        listFragment.add(new Fragment1());
        listFragment.add(new Fragment2());
        listFragment.add(new Fragment3());
        listFragment.add(new Fragment4());
        MyFragAdapter myAdapter = new MyFragAdapter(getSupportFragmentManager(), this, listFragment);
        viewPager.setAdapter(myAdapter);

        //导航栏点击事件和ViewPager滑动事件,让两个控件相互关联
        navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                //这里设置为:当点击到某子项,ViewPager就滑动到对应位置
                switch (item.getItemId()) {
                    case R.id.navigation_home:
                        viewPager.setCurrentItem(0);
                        return true;
                    case R.id.navigation_dashboard:
                        viewPager.setCurrentItem(1);
                        return true;
                    case R.id.navigation_notifications:
                        viewPager.setCurrentItem(2);
                        return true;
                    case R.id.navigation_setting:
                        viewPager.setCurrentItem(3);
                        return true;
                    default:
                        break;
                }
                return false;
            }
        });

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                //注意这个方法滑动时会调用多次,下面是参数解释:
                //position当前所处页面索引,滑动调用的最后一次绝对是滑动停止所在页面
                //positionOffset:表示从位置的页面偏移的[0,1]的值。
                //positionOffsetPixels:以像素为单位的值,表示与位置的偏移
            }

            @Override
            public void onPageSelected(int position) {
                //该方法只在滑动停止时调用,position滑动停止所在页面位置
//                当滑动到某一位置,导航栏对应位置被按下
                navigation.getMenu().getItem(position).setChecked(true);
                //这里使用navigation.setSelectedItemId(position);无效,
                //setSelectedItemId(position)的官网原句:Set the selected
                // menu item ID. This behaves the same as tapping on an item
                //未找到原因
            }

            @Override
            public void onPageScrollStateChanged(int state) {
//这个方法在滑动是调用三次,分别对应下面三种状态
// 这个方法对于发现用户何时开始拖动,
// 何时寻呼机自动调整到当前页面,或何时完全停止/空闲非常有用。
//                state表示新的滑动状态,有三个值:
//                SCROLL_STATE_IDLE:开始滑动(空闲状态->滑动),实际值为0
//                SCROLL_STATE_DRAGGING:正在被拖动,实际值为1
//                SCROLL_STATE_SETTLING:拖动结束,实际值为2
            }
        });
    }

}

猜你喜欢

转载自blog.csdn.net/htwhtw123/article/details/78441431