ViewPager用法详细解析

ViewPager详解

  • ViewPager中的主要方法详解
  • OnPageChangeListener中的三个方法详解
  • 三种适配器的使用及其主要方法详解

ViewPager用于实现页面间的切换。

ViewPager中的主要方法详解

  • setAdapter(PagerAdapter adapter) 
    该方法为ViewPager设置适配器,ViewPager有三种适配器,它们分别有不同的特性,下面我会对这三种适配器进行讲解。
  • setCurrentItem(int item) 
    该方法设置显示item位置的界面。
  • setOffscreenPageLimit(int limit) 
    该方法用来设置当前显示页面左右两边缓存的页面数。
  • addOnPageChangeListener(OnPageChangeListener listener) 
    该方法为ViewPager添加页面切换时的监听,关于界面监听的内容,接下来对OnPageChangeListener中的方法进行讲解时,再详细说明。
  • setOnScrollChangeListener(OnScrollChangeListener l) 
    该方法为ViewPager增加滚动状态监听,但该方法需要minSdkVersion为23

OnPageChangeListener中的三个方法详解

  • onPageScrollStateChanged(int state) 
    该方法在手指操作屏幕的时候发生变化。有三个值:0(END),1(PRESS) ,2(UP) 。当用手指滑动翻页时,手指按下去的时候会触发这个方法,state值为1,手指抬起时,如果发生了滑动(即使很小),这个值会变为2,然后最后变为0 。总共执行这个方法三次。一种特殊情况是手指按下去以后一点滑动也没有发生,这个时候只会调用这个方法两次,state值分别是1,0 。当setCurrentItem翻页时,会执行这个方法两次,state值分别为2 ,0 。
  • onPageScrolled(int position, float positionOffset, int positionOffsetPixels) 
    该方法在滑动过程中将一直被调用,该方法的参数说明如下: 
    position:当用手指滑动时,如果手指按在页面上不动,position和当前页面index是一致的;如果手指向左拖动(相应页面向右翻动),这时候position大部分时间和当前页面是一致的,只有翻页成功的情况下最后一次调用才会变为目标页面;如果手指向右拖动(相应页面向左翻动),这时候position大部分时间和目标页面是一致的,只有翻页不成功的情况下最后一次调用才会变为原页面。当直接设置setCurrentItem翻页时,如果是相邻的情况(比如现在是第二个页面,跳到第一或者第三个页面),如果页面向右翻动,大部分时间是和当前页面是一致的,只有最后才变成目标页面;如果向左翻动,position和目标页面是一致的。这和用手指拖动页面翻动是基本一致的。如果不是相邻的情况,比如我从第一个页面跳到第三个页面,position先是0,然后逐步变成1,然后逐步变成2;我从第三个页面跳到第一个页面,position先是1,然后逐步变成0,并没有出现为2的情况。 
    positionOffset:当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。 
    positionOffsetPixels:当前页面滑动像素,变化情况和positionOffset一致
  • onPageSelected(int position) 
    position是被选中页面的索引,该方法在页面被选中或页面滑动足够距离切换到该页手指抬起时调用。

三个方法的执行顺序:用手指拖动翻页时,最先执行一遍onPageScrollStateChanged(1),然后不断执行onPageScrolled,放手指的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)

三种适配器的使用及其主要方法详解

三种适配器继承关系

这里写图片描述

PagerAdapter
主要方法详解
  • public abstract int getCount () 
    返回有效视图的数量。
  • public int getItemPosition (Object object) 
    当宿主视图尝试判断一项的位置是否改变时调用。如果给定项的位置没有改变则返回POSITION_UNCHANGED,如果该项不再存在于适配器中则返回POSITION_NONE。 
    在ViewPager.dataSetChanged()中将对该函数的返回值进行判断,如果返回POSITION_NONE则调用destroyItem(ViewGroup container, int position, Object object)方法将该视图销毁,如果返回POSITION_UNCHANGED则不做任何改变,如果数据改变,则触发PagerAdapter.instantiateItem(ViewGroup container, int position)方法改变视图。 
    PagerAdapter中该方法的默认返回值是 POSITION_UNCHANGED。如果没有重载该函数,而导致调用PagerAdapter.notifyDataSetChanged() 后,什么都没有发生。
  • public boolean isViewFromObject (View view, Object object) 
    决定一个页面view是否与instantiateItem(ViewGroup, int)方法返回的具体key对象相关联。 
    viewpager不直接处理每一个视图而是将各个视图与一个键联系起来。这个键用来跟踪且唯一代表一个页面,不仅如此,该键还独立于这个页面所在adapter的位置。当pageradapter将要改变的时候他会调用startUpdate函数,接下来会调用一次或多次的instantiateItem或者destroyItem。最后在更新的后期会调用finishUpdate。当finishUpdate返回时instantiateItem返回的对象应该添加到父ViewGroup,destroyItem返回的对象应该被ViewGroup删除。isViewFromObject(View, Object)代表了当前的页面是否与给定的键相关联。 
    自定义Key示例-简单的将位置position最为key
private class MyPagerAdapter extends PagerAdapter {

        private List<View> mViewList;

        MyPagerAdapter(List<View> viewList) {
            mViewList = viewList;
        }

        @Override
        public int getCount() {
            Log.i(TAG, NAME + "--getCount");
            return mViewList.size();
        }

        @Override
        public int getItemPosition(Object object) { 
            return super.getItemPosition(object);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            Log.i(TAG, NAME + "--isViewFromObject");
            return view == mViewList.get((int)Integer.parseInt(object.toString()));
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = mViewList.get(position);
            container.addView(view);
            Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
            return position;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(mViewList.get(position));
            Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • public Object instantiateItem (ViewGroup container, int position) 
    创建指定位置的页面视图。适配器有责任增加即将创建的View视图到给定的container中,确保在finishUpdate(viewGroup)返回时,增加视图的事情已经完成。 
    该方法的返回值是新增视图页面的Object(Key),这里没必要非要返回视图本身,也可以是这个页面的其它容器,它可以返回和视图相关联的任何值。
  • public void destroyItem (ViewGroup container, int position, Object object) 
    移除给定位置的view,适配器有责任将该view从container中移除,确保在finishUpdate(viewGroup)返回时,移除视图的事情已经完成。
  • public void startUpdate (ViewGroup container) 
    在展示的界面中有改变将要发生时调用。
  • public void finishUpdate (ViewGroup container) 
    展示界面中的改变完成时调用。在这个时间点上,你必须确保所有的页面已被合适的从container中添加或移除。
  • public void notifyDataSetChanged () 
    该方法由应用程序在适配器数据改变时主动调用。
  • public void registerDataSetObserver (DataSetObserver observer) 
    注册一个观察者去接收关联到适配器数据变化的回调。
  • public void unregisterDataSetObserver (DataSetObserver observer) 
    反注册去接收关联到适配器数据变化的回调的观察者。
  • public void setPrimaryItem (ViewGroup container, int position, Object object) 
    调用该方法去通知当前适配器的哪一项被考虑为“primary”,它是当前展示给用户的页面。
  • public CharSequence getPageTitle (int position) 
    该方法由ViewPager在获取描述页面的标题时调用。该方法默认返回null。
  • public float getPageWidth (int position) 
    该方法返回给定页面的比例宽度,范围(0.f-1.f]。
  • public Parcelable saveState () 
    保存与适配器关联的实例状态,当当前UI状态需要重建时恢复。
  • public void restoreState (Parcelable state, ClassLoader loader) 
    恢复之前由saveState ()保存的与适配器关联的实例状态。
PagerAdapter使用示例

activity_pager_adapter.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"
    tools:context="com.example.sunxiaodong.viewpager.PagerAdapterActivity">

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

    </android.support.v4.view.ViewPager>

    <TextView
        android:id="@+id/page_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:textColor="#ffffff"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true">

        <EditText
            android:id="@+id/edit_text"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_gravity="center_vertical"
            android:textColor="#000000"
            android:textSize="20sp" />

        <Button
            android:id="@+id/button"
            android:layout_width="80dp"
            android:layout_height="40dp"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp"
            android:text="跳转"
            android:textColor="#ffffff" />
    </LinearLayout>

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

page1.xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff0000">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="page1"
        android:textColor="#ffffff"
        android:textSize="20sp" />

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

page2.xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff00ff">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="page2"
        android:textColor="#ffffff"
        android:textSize="20sp" />

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

page3.xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00ff00">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="page3"
        android:textColor="#ffffff"
        android:textSize="20sp" />

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

page4.xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffff00">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="page4"
        android:textColor="#ffffff"
        android:textSize="20sp" />

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

page5.xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#8e35ef">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="page5"
        android:textColor="#ffffff"
        android:textSize="20sp" />

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

PagerAdapterActivity.java文件

public class PagerAdapterActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = PagerAdapterActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private ViewPager mViewPager;
    private MyPagerAdapter mMyPagerAdapter;
    private TextView mPageNum;
    private EditText mEditText;
    private Button mButton;

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

    private void initView() {
        mViewPager = (ViewPager) this.findViewById(R.id.viewpager);
        mPageNum = (TextView) this.findViewById(R.id.page_num);
        mEditText = (EditText) this.findViewById(R.id.edit_text);
        mButton = (Button) this.findViewById(R.id.button);
        mButton.setOnClickListener(this);

        List<View> viewList = new ArrayList<View>();

        LayoutInflater layoutInflater = getLayoutInflater();
        View view1 = layoutInflater.inflate(R.layout.page1, null);
        View view2 = layoutInflater.inflate(R.layout.page2, null);
        View view3 = layoutInflater.inflate(R.layout.page3, null);
        View view4 = layoutInflater.inflate(R.layout.page4, null);
        View view5 = layoutInflater.inflate(R.layout.page5, null);

        viewList.add(view1);
        viewList.add(view2);
        viewList.add(view3);
        viewList.add(view4);
        viewList.add(view5);
        mMyPagerAdapter = new MyPagerAdapter(viewList);
        mViewPager.setAdapter(mMyPagerAdapter);
        mViewPager.addOnPageChangeListener(new OnMyPageChangeListener());//页面变化监听
        mViewPager.setOffscreenPageLimit(2);//设置缓存页面数。当前页,左右两边(单边)最大缓存页面数。
//        mViewPager.setOnScrollChangeListener(new OnMyScrollChangeListener());//滚动状态监听,minSdkVersion:23
//        mViewPager.getCurrentItem();//获取当前显示页索引
//        mViewPager.getOffscreenPageLimit();//获取缓存页面数
//        mViewPager.onSaveInstanceState();
//        mViewPager.setPageTransformer();
        mMyPagerAdapter.notifyDataSetChanged();
        setPageNum(0);//设置显示首页
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                goPage();
                break;
        }
    }

    private void goPage() {
        String pageNumStr = mEditText.getText().toString();
        if (pageNumStr == null || pageNumStr.isEmpty()) {
            return;
        }
        int pageNum = Integer.parseInt(pageNumStr);
        if (pageNum > 0 && pageNum <= mMyPagerAdapter.getCount()) {
            mViewPager.setCurrentItem(pageNum - 1);//设置当前显示页索引
        }
    }

    private void setPageNum(int position) {
        String pageNum = (position + 1) + "/" + mMyPagerAdapter.getCount();
        mPageNum.setText(pageNum);
    }

    /*private class OnMyScrollChangeListener implements View.OnScrollChangeListener {

        @Override
        public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {

        }
    }*/

    /**
     * 页面变化监听器
     */
    private class OnMyPageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            Log.i(TAG, NAME + "--onPageScrolled++position:" + position + ",++positionOffset:" + positionOffset + ",++positionOffsetPixels:" + positionOffsetPixels);
        }

        @Override
        public void onPageSelected(int position) {
            Log.i(TAG, NAME + "--onPageSelected++position:" + position);
            setPageNum(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            Log.i(TAG, NAME + "--onPageScrollStateChanged++state:" + state);
        }
    }

    /**
     * 页面适配器
     */
    private class MyPagerAdapter extends PagerAdapter {

        private List<View> mViewList;

        MyPagerAdapter(List<View> viewList) {
            mViewList = viewList;
        }

        @Override
        public int getCount() {
            Log.i(TAG, NAME + "--getCount");
            return mViewList.size();
        }

        @Override
        public int getItemPosition(Object object) {
            Log.i(TAG, NAME + "--getItemPosition");
            return super.getItemPosition(object);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            Log.i(TAG, NAME + "--isViewFromObject");
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = mViewList.get(position);
            container.addView(view);
            Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(mViewList.get(position));
            Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
        }

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
FragmentPagerAdapter

  该适配器最好用于有限个静态fragment页面的管理。尽管不可见的视图有时会被销毁,但用户所有访问过的fragment都会被保存在内存中。因此fragment实例会保存大量的各种状态,这就造成了很大的内存开销。 
  使用该适配器,ViewPager在进行Fragment界面切换时,会将超过缓存数的界面销毁,但不销毁数据,即调用Fragment的onDestoryView方法,所有创建过的Fragment都会被保留。

主要方法详解
  • public abstract Fragment getItem (int position) 
    返回position位置关联的Fragment。 
    FragmentPagerAdapter会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。该方法只有在创建新的Fragment时才调用。 
    在需要时,该函数将被 instantiateItem() 所调用。 
    如果需要向 Fragment 对象传递相对静态的数据时,我们一般通过 Fragment.setArguments() 来进行,这部分代码应当放到 getItem()。它们只会在新生成 Fragment 对象时执行一遍。
  • public long getItemId (int position) 
    返回给定位置项的唯一标识。
  • public Object instantiateItem (ViewGroup container, int position) 
    该方法每次生成缓存页面之外的Fragment时都会调用,这里的Fragment可能是新的生成,也可能是恢复。 
    函数中判断一下要生成的Fragment是否已经生成过了,如果生成过了,就使用旧的,旧的将被Fragment.attach();如果没有,就调用getItem()生成一个新的,新的对象将被FragmentTransation.add()。 
    FragmentPagerAdapter会将所有生成的Fragment对象通过FragmentManager保存起来备用,以后需要该Fragment时,都会从FragmentManager读取,而不会再次调用getItem()方法。 
    如果需要在生成Fragment对象后,将数据集中的一些数据传递给该Fragment,这部分代码应该放到这个函数的重载里。在我们继承的子类中,重载该函数,并调用FragmentPagerAdapter.instantiateItem()取得该函数返回 Fragment 对象,然后,我们该Fragment对象中对应的方法,将数据传递过去,然后返回该对象。 
    否则,如果将这部分传递数据的代码放到getItem()中,在PagerAdapter.notifyDataSetChanged()后,这部分数据设置代码将不会被调用。
  • public void destroyItem(ViewGroup container, int position, Object object) 
    超出缓存的页面,将调用该方法从视图中移除。 
    该函数被调用后,会对Fragment进行FragmentTransaction.detach()。这里不是remove(),只是detach(),因此Fragment还在FragmentManager管理中,Fragment所占用的资源不会被释放。
FragmentPagerAdapter使用示例

FragmentPagerAdapterActivity.java文件

/**
 * Created by sunxiaodong on 16/1/27.
 * 该适配器最好用于有限个静态fragment页面的管理。尽管不可见的视图有时会被销毁,但用户所有访问过的fragment都会被保存在内存中。因此fragment实例会保存大量的各种状态,这就造成了很大的内存开销。
 * 所以如果要处理大量的页面切换,建议使用FragmentStatePagerAdapter.
 */
public class FragmentPagerAdapterActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String NAME = FragmentPagerAdapterActivity.class.getSimpleName();
    private static final String TAG = "sxd";

    private ViewPager mViewPager;
    private MyFragmentPagerAdapter mMyFragmentPagerAdapter;
    private TextView mPageNum;
    private EditText mEditText;
    private Button mButton;

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

    private void initView() {
        mViewPager = (ViewPager) this.findViewById(R.id.viewpager);
        mPageNum = (TextView) this.findViewById(R.id.page_num);
        mEditText = (EditText) this.findViewById(R.id.edit_text);
        mButton = (Button) this.findViewById(R.id.button);
        mButton.setOnClickListener(this);

        List<Fragment> fragments = new ArrayList<Fragment>();
        Fragment fragment1 = MyFragment.newInstance(1);
        Fragment fragment2 = MyFragment.newInstance(2);
        Fragment fragment3 = MyFragment.newInstance(3);
        Fragment fragment4 = MyFragment.newInstance(4);
        Fragment fragment5 = MyFragment.newInstance(5);


        fragments.add(fragment1);
        fragments.add(fragment2);
        fragments.add(fragment3);
        fragments.add(fragment4);
        fragments.add(fragment5);

        mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments);
        mViewPager.setAdapter(mMyFragmentPagerAdapter);
        mViewPager.addOnPageChangeListener(new OnMyPageChangeListener());//页面变化监听
        mViewPager.setOffscreenPageLimit(2);//设置缓存页面数。当前页,左右两边(单边)最大缓存页面数。
//        mViewPager.setOnScrollChangeListener(new OnMyScrollChangeListener());//滚动状态监听,minSdkVersion:23
//        mViewPager.getCurrentItem();//获取当前显示页索引
//        mViewPager.getOffscreenPageLimit();//获取缓存页面数
//        mViewPager.onSaveInstanceState();
//        mViewPager.setPageTransformer();
        setPageNum(0);//设置显示首页
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                goPage();
                break;
        }
    }

    private void goPage() {
        String pageNumStr = mEditText.getText().toString();
        if (pageNumStr == null || pageNumStr.isEmpty()) {
            return;
        }
        int pageNum = Integer.parseInt(pageNumStr);
        if (pageNum > 0 && pageNum <= mMyFragmentPagerAdapter.getCount()) {
            mViewPager.setCurrentItem(pageNum - 1);//设置当前显示页索引
        }
    }

    private void setPageNum(int position) {
        String pageNum = (position + 1) + "/" + mMyFragmentPagerAdapter.getCount();
        mPageNum.setText(pageNum);
    }

    /**
     * 页面变化监听器。
     * 三个方法的执行顺序为:用手指拖动翻页时,最先执行一遍onPageScrollStateChanged(1),然后不断执行onPageScrolled,放手指的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)。
     */
    private class OnMyPageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            //position:当用手指滑动时,如果手指按在页面上不动,position和当前页面index是一致的;如果手指向左拖动(相应页面向右翻动),这时候position大部分时间和当前页面是一致的,只有翻页成功的情况下最后一次调用才会变为目标页面;如果手指向右拖动(相应页面向左翻动),这时候position大部分时间和目标页面是一致的,只有翻页不成功的情况下最后一次调用才会变为原页面。
            //当直接设置setCurrentItem翻页时,如果是相邻的情况(比如现在是第二个页面,跳到第一或者第三个页面),如果页面向右翻动,大部分时间是和当前页面是一致的,只有最后才变成目标页面;如果向左翻动,position和目标页面是一致的。这和用手指拖动页面翻动是基本一致的。
            //如果不是相邻的情况,比如我从第一个页面跳到第三个页面,position先是0,然后逐步变成1,然后逐步变成2;我从第三个页面跳到第一个页面,position先是1,然后逐步变成0,并没有出现为2的情况。
            //positionOffset:当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。
            //positionOffsetPixels:当前页面滑动像素,变化情况和positionOffset一致。
            Log.i(TAG, NAME + "--onPageScrolled++position:" + position + ",++positionOffset:" + positionOffset + ",++positionOffsetPixels:" + positionOffsetPixels);
        }

        @Override
        public void onPageSelected(int position) {
            //position是被选中页面的索引,该方法在页面被选中或页面滑动足够距离切换到该页手指抬起时调用。
            Log.i(TAG, NAME + "--onPageSelected++position:" + position);
            setPageNum(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            //这个方法在手指操作屏幕的时候发生变化。有三个值:0(END),1(PRESS) , 2(UP) 。
            //当用手指滑动翻页时,手指按下去的时候会触发这个方法,state值为1,手指抬起时,
            //如果发生了滑动(即使很小),这个值会变为2,然后最后变为0 。总共执行这个方法三次。
            //一种特殊情况是手指按下去以后一点滑动也没有发生,这个时候只会调用这个方法两次,state值分别是1,0 。
            //当setCurrentItem翻页时,会执行这个方法两次,state值分别为2 , 0 。
            Log.i(TAG, NAME + "--onPageScrollStateChanged++state:" + state);
        }
    }

    /**
     * Fragment页面适配器
     * ViewPager在进行Fragment界面切换时,会将超过缓存数的界面销毁,但不销毁数据,即调用Fragment的onDestoryView方法
     * 所有创建过的Fragment都会被保留
     */
    private class MyFragmentPagerAdapter extends FragmentPagerAdapter {

        private List<Fragment> mFragments;

        MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
            super(fm);
            mFragments = fragments;
        }

        @Override
        public Fragment getItem(int position) {
            //FragmentPagerAdapter 会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,
            // 都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。该方法只有在创建新的Fragment时才调用。
            //1. 在需要时,该函数将被 instantiateItem() 所调用。
            //2. 如果需要向 Fragment 对象传递相对静态的数据时,我们一般通过 Fragment.setArguments() 来进行,这部分代码应当放到 getItem()。它们只会在新生成 Fragment 对象时执行一遍。
            Log.i(TAG, NAME + "--getItem++position:" + position);
            return mFragments.get(position);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //该方法每次生成缓存页面之外的Fragment时都会调用,这里的Fragment可能是新的生成,也可能是恢复
            //1.函数中判断一下要生成的 Fragment 是否已经生成过了,如果生成过了,就使用旧的,旧的将被 Fragment.attach();如果没有,就调用 getItem() 生成一个新的,新的对象将被 FragmentTransation.add()。
            //2. FragmentPagerAdapter 会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。
            //3. 如果需要在生成 Fragment 对象后,将数据集中的一些数据传递给该 Fragment,这部分代码应该放到这个函数的重载里。在我们继承的子类中,重载该函数,并调用 FragmentPagerAdapter.instantiateItem() 取得该函数返回 Fragment 对象,然后,我们该 Fragment 对象中对应的方法,将数据传递过去,然后返回该对象。
            //否则,如果将这部分传递数据的代码放到 getItem()中,在 PagerAdapter.notifyDataSetChanged() 后,这部分数据设置代码将不会被调用。
            Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
            return super.instantiateItem(container, position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            //超出缓存的页面,将调用该方法从视图中移除
            //该函数被调用后,会对 Fragment 进行 FragmentTransaction.detach()。这里不是 remove(),只是 detach(),因此 Fragment 还在 FragmentManager 管理中,Fragment 所占用的资源不会被释放。
            Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
            super.destroyItem(container, position, object);
        }

        @Override
        public int getCount() {
            Log.i(TAG, NAME + "--getCount++");
            return mFragments.size();
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
FragmentStatePagerAdapter

  该适配器适合处理大量的页面切换,其能能够保存和恢复Fragment状态的适配器,超出缓存的Fragment会调用onDestory()方法,彻底对销毁Fragment进行销毁。

主要方法详解
  • public Fragment getItem(int position) 
    返回position位置关联的Fragment。 
    该方法在滑到已经缓存的页面时,并不被调用。缓存外已经创建过并被销毁的页面,还会再调用该方法,重新创建。
  • public Object instantiateItem(ViewGroup container, int position) 
    除非碰到FragmentManager刚好从SavedState中恢复了对应的Fragment的情况外(从页面缓存中恢复),该函数将会调用getItem()函数,生成新的Fragment对象。新的对象将被FragmentTransaction.add()
  • public void destroyItem(ViewGroup container, int position, Object object) 
    超出缓存的页面,将调用该方法从视图中移除。
FragmentStatePagerAdapter使用示例
/**
     * 能够保存和恢复Fragment状态的适配器
     * 超出缓存的Fragment会调用onDestory()方法,彻底对销毁Fragment进行销毁
     */
    class MyFragmentStatePagerAdapter extends FragmentStatePagerAdapter {

        private List<Fragment> mFragments;

        MyFragmentStatePagerAdapter(FragmentManager fm, List<Fragment> fragments) {
            super(fm);
            mFragments = fragments;
        }

        @Override
        public Fragment getItem(int position) {
            //该方法在滑到已经缓存的页面时,并不被调用。缓存外已经创建过并被销毁的页面,还会再调用该方法,重新创建。
            Log.i(TAG, NAME + "--getItem++position:" + position);
            return mFragments.get(position);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            //除非碰到 FragmentManager 刚好从 SavedState 中恢复了对应的 Fragment 的情况外(从页面缓存中恢复),该函数将会调用 getItem() 函数,生成新的 Fragment 对象。新的对象将被 FragmentTransaction.add()。
            Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
            return super.instantiateItem(container, position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            //超出缓存的页面,将调用该方法从视图中移除
            Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
            super.destroyItem(container, position, object);
        }

        @Override
        public int getCount() {
            Log.i(TAG, NAME + "--getCount++");
            return mFragments.size();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

源码地址

猜你喜欢

转载自blog.csdn.net/fyq520521/article/details/80595684