ViewPager+Fragment实现底部导航栏(左右滑动/点击切换)

声明:
本博文主要参考和借鉴了5.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面

和原博文的区别:

1.针对Demo中的 代码进行了部分优化
2.针对原博文中的”ViewPager的缓存机制“部分,通过log日志形式进行详细说明
3.针对原博文中下列几种ViewPager左右切换过程中的常量值的错误说明进行更正!

ViewPager.SCROLL_STATE_IDLE
ViewPager.SCROLL_STATE_DRAGGING
ViewPager.SCROLL_STATE_SETTING

Demo效果图:
这里写图片描述

项目结构:
这里写图片描述

代码实现:
Step 1:相关布局文件layout
(1).activity.xml

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

    <RelativeLayout
        android:id="@+id/title_bar"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="@color/bg_title_bar">

        <TextView
            android:id="@+id/title_bar_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="xx"
            android:textColor="@color/text_title_bar"
            android:textSize="18sp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_alignParentBottom="true"
            android:background="@color/div_white" />

    </RelativeLayout>


    <RadioGroup
        android:id="@+id/rg_tab_bar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_alignParentBottom="true"
        android:background="@color/bg_white"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/rb_channel"
            style="@style/tab_menu_item"
            android:drawableTop="@drawable/tab_menu_channel"
            android:text="@string/tab_menu_alert" />

        <RadioButton
            android:id="@+id/rb_message"
            style="@style/tab_menu_item"
            android:drawableTop="@drawable/tab_menu_message"
            android:text="@string/tab_menu_profile" />

        <RadioButton
            android:id="@+id/rb_better"
            style="@style/tab_menu_item"
            android:drawableTop="@drawable/tab_menu_better"
            android:text="@string/tab_menu_pay" />

        <RadioButton
            android:id="@+id/rb_setting"
            style="@style/tab_menu_item"
            android:drawableTop="@drawable/tab_menu_setting"
            android:text="@string/tab_menu_setting" />

    </RadioGroup>

    <View
        android:id="@+id/div_tab_bar"
        android:layout_width="match_parent"
        android:layout_height="2px"
        android:layout_above="@id/rg_tab_bar"
        android:background="@color/div_white" />

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


</RelativeLayout>

fg_content.xml(4个Fragment共用此布局文件)

<?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"
    android:background="@color/bg_white"
    android:orientation="vertical">

    <TextView
        android:id="@+id/txt_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="XX"
        android:textColor="@color/text_yellow"
        android:textSize="20sp" />

</LinearLayout>

Step 2:4个基本相同的Fragment


public class MyFragment1 extends Fragment {
    private static final String TAG = "MyFragment1";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fg_content, container, false);
        TextView txt_content = (TextView) view.findViewById(R.id.txt_content);
        txt_content.setText("第一个Fragment");
        Log.i(TAG, "onCreateView: 0000--------MyFragment1");
        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.i(TAG, "onDestroyView: 0000-------MyFragment1");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: 0000--------MyFragment1");
    }

    public MyFragment1() {
    }

}

Step 3:主程序MainActivity

public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener,
        ViewPager.OnPageChangeListener {
    private static final String TAG = "MainActivity";

    public static final int PAGE_ONE = 0;
    public static final int PAGE_TWO = 1;
    public static final int PAGE_THREE = 2;
    public static final int PAGE_FOUR = 3;


    private TextView titleBarText;
    private RadioGroup rgTabBar;
    private RadioButton rbChannel;
    private RadioButton rbMessage;
    private RadioButton rbBetter;
    private RadioButton rbSetting;
    private ViewPager viewpager;
    private MyFragmentPagerAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
        initViews();
    }

    private void initViews() {

        titleBarText = (TextView) findViewById(R.id.title_bar_text);
        rgTabBar = (RadioGroup) findViewById(R.id.rg_tab_bar);
        rbChannel = (RadioButton) findViewById(R.id.rb_channel);
        rbMessage = (RadioButton) findViewById(R.id.rb_message);
        rbBetter = (RadioButton) findViewById(R.id.rb_better);
        rbSetting = (RadioButton) findViewById(R.id.rb_setting);
        viewpager = (ViewPager) findViewById(R.id.viewpager);

        rgTabBar.setOnCheckedChangeListener(this);
        viewpager.setAdapter(mAdapter);
        viewpager.setCurrentItem(0);
        viewpager.addOnPageChangeListener(this);
        rbChannel.setChecked(true);

    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        switch (checkedId) {
            case R.id.rb_channel:
                viewpager.setCurrentItem(PAGE_ONE);
                titleBarText.setText("提醒");
                break;
            case R.id.rb_message:
                viewpager.setCurrentItem(PAGE_TWO);
                titleBarText.setText("信息");
                break;
            case R.id.rb_better:
                viewpager.setCurrentItem(PAGE_THREE);
                titleBarText.setText("我的");
                break;
            case R.id.rb_setting:
                viewpager.setCurrentItem(PAGE_FOUR);
                titleBarText.setText("设置");
                break;
        }
    }


    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
    }

    @Override
    public void onPageScrollStateChanged(int state) {

        /**
         * 原博文中注释(state的状态有三个,0表示什么都没做,1正在滑动,2滑动完毕)有误
         * 特此更正为如下:
         *
         *SCROLL_STATE_IDLE : 值为0,表示当前页已经选定。
         *SCROLL_STATE_DRAGGING: 值为1,表示当前页面正在拖动。
         *SCROLL_STATE_SETTING: 值为2,表示当前页面正在拖动中,还没有到选定状态。
         */
        Log.i(TAG, "onPageScrollStateChanged: state::" + state);
        if (state == 2) {
            int currentItemPosition = viewpager.getCurrentItem();
            Log.w(TAG, "onPageScrollStateChanged: currentItemPosition::" + currentItemPosition);
            switch (currentItemPosition) {
                case PAGE_ONE:
                    rbChannel.setChecked(true);
                    titleBarText.setText("提醒");
                    break;
                case PAGE_TWO:
                    rbMessage.setChecked(true);
                    titleBarText.setText("信息");
                    break;
                case PAGE_THREE:
                    rbBetter.setChecked(true);
                    titleBarText.setText("我的");
                    break;
                case PAGE_FOUR:
                    rbSetting.setChecked(true);
                    titleBarText.setText("设置");
                    break;
            }
        }
    }

}

上述MainActivity中的代码中,ViewPager的onPageScrollStateChanged(int state)中必须加上

if (state == 2) {

}
的判断,虽然不加也能得到正确的结果,但是通过log你会发现if判断中的逻辑会调用很多次,因为当你刚开始拖动(SCROLL_STATE_DRAGGING: 值为1,表示当前页面正在拖动。),以及拖动过程结束手刚离开屏幕(SCROLL_STATE_SETTING: 值为2,表示当前页面正在拖动中,还没有到选定状态。),再到选定到当前的fragment界面(SCROLL_STATE_IDLE : 值为0,表示当前页已经选定。)都会调用…这样会很浪费系统性能。
执行顺序是:
1:如果state==1,则手刚开始滑动,底边导航栏就会进行切换
2:正常
0:如果state-==0,则页面停留,不再变化之后,底边导航栏才会进行切换,滞后感十分明显
针对ViewPager中的三个常量值,代码注释中已经详细说明了,在此不在赘述。

Step 4:ViewPager适配器(自定义FragmentPagerAdapter类)
代码很简单,只需传递一个FragmentManager过来,其他都在这里完成!

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    private final int PAGER_COUNT = 4;
    private MyFragment1 myFragment1;
    private MyFragment2 myFragment2;
    private MyFragment3 myFragment3;
    private MyFragment4 myFragment4;


    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        myFragment1 = new MyFragment1();
        myFragment2 = new MyFragment2();
        myFragment3 = new MyFragment3();
        myFragment4 = new MyFragment4();
    }


    @Override
    public int getCount() {
        return PAGER_COUNT;
    }

    @Override
    public Object instantiateItem(ViewGroup vg, int position) {
        return super.instantiateItem(vg, position);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        System.out.println("position Destory" + position);
        super.destroyItem(container, position, object);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        switch (position) {
            case MainActivity.PAGE_ONE:
                fragment = myFragment1;
                break;
            case MainActivity.PAGE_TWO:
                fragment = myFragment2;
                break;
            case MainActivity.PAGE_THREE:
                fragment = myFragment3;
                break;
            case MainActivity.PAGE_FOUR:
                fragment = myFragment4;
                break;
        }
        return fragment;
    }


}

使用PagerAdapter要重写相关方法:

  • getCount( ):获得viewpager中有多少个view

  • destroyItem( ):移除一个给定位置的页面。适配器有责任从容器中删除这个视图。这是为了确保 在finishUpdate(viewGroup)返回时视图能够被移除。

  • instantiateItem( ):①将给定位置的view添加到ViewGroup(容器)中,创建并显示出来
    ②返回一个代表新增页面的Object(key),通常都是直接返回view本身就可以了, 当然你也可以自定义自己的key,但是key和每个view要一一对应的关系

  • isViewFromObject( ):判断instantiateItem(ViewGroup, int)函数所返回来的Key与一个页面视图是否是 代表的同一个视图(即它俩是否是对应的,对应的表示同一个View),通常我们直接写 return view == object;就可以了,至于为什么要这样讲起来比较复杂,后面有机会进行了解吧 貌似是ViewPager中有个存储view状态信息的ArrayList,根据View取出对应信息的吧!

至此,ViewPager+Fragment实现底部导航栏完成。
下面针对ViewPager的缓存机制做下说明:
直接引用原博文说明。

一个页面切换的组件,我们可以往里面填充多个View,然后我们可以通过触摸屏幕左右滑动 切换不同的View,和前面学习的ListView一样,我们需要一个Adapter(适配器),将要显示的View和 我们的ViewPager进行绑定,而ViewPager有他自己特定的Adapter——PagerAdapter!另外,Google 官方是建议我们使用Fragment来填充ViewPager的,这样可以更加方便的生成每个Page以及管理 每个Page的生命周期!当然它给我们提供了两个不同的Adapter,他们分别是:FragmentPageAdapterFragmentStatePagerAdapter! 而我们本节用到的则是前者:FragmentPageAdapter!
另外要说一点的是ViewPager的缓存机制ViewPager会缓存当前页,前一页,以及后一页,比如有1,2,3,4这四个页面: 当我们
——>处于第一页:缓存1,2
——> 处于第二页:缓存 1,2,3
——> 处于第三页:缓存2,3,4
——> 处于第四页缓存3,4这样!

详情见log截图,自然明了。
ViewPager缓存机制
至此,完结,如有疑问请留言告知。
后续会出一篇关于FragmentPageAdapter和FragmentStatePagerAdapter的区别的文章。

附上demo链接(本来不想加积分的,但是csdn上传资源时,好像最小1积分,请谅解)。
https://download.csdn.net/download/zhangqunshuai/10459362

猜你喜欢

转载自blog.csdn.net/zhangqunshuai/article/details/80576654
今日推荐