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

    }
  • 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>

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>

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>

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>

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>

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>

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

    }

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

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

源碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章