日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ViewPager刷新问题详解

發(fā)布時間:2025/4/16 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ViewPager刷新问题详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文鏈接:簡書diygreen,http://www.jianshu.com/p/266861496508

一、PagerAdapter介紹

先看效果圖

PagerAdapter簡介

ListView 大家應該都很熟悉吧!ListView 一般都需要一個 Adapter 來填充數(shù)據(jù),如 ArrayAdapter、SimpleAdapter。PagerAdapter 就是 ViewPager 的 Adapter,與 ListView 的 Adapter 作用一樣。

ViewPager->PageAdapter == ListView->BaseAdapter

先看下官方介紹

PageAdapter 繼承自 Object,繼承結構參考意義不大,那老實看文檔。文檔上沒有提供示例代碼,只是說了下要自定義 PageAdapter 需要實現(xiàn)下面四個方法:

  • instantiateItem(ViewGroup container, int position)

該方法的功能是創(chuàng)建指定位置的頁面視圖。適配器有責任增加即將創(chuàng)建的 View 視圖到這里給定的 container 中,這是為了確保在 finishUpdate(viewGroup) 返回時 this is be done!
返回值:返回一個代表新增視圖頁面的 Object(Key),這里沒必要非要返回視圖本身,也可以這個頁面的其它容器。其實我的理解是可以代表當前頁面的任意值,只要你可以與你增加的 View 一一對應即可,比如 position 變量也可以做為 Key

  • destroyItem(ViewGroup container, int position, Object object)

該方法的功能是移除一個給定位置的頁面。適配器有責任從容器中刪除這個視圖,這是為了確保在 finishUpdate(viewGroup) 返回時視圖能夠被移除

  • getCount():返回當前有效視圖的數(shù)量

  • isViewFromObject(View view, Object object)

該函數(shù)用來判斷 instantiateItem() 函數(shù)所返回來的 Key 與一個頁面視圖是否是代表的同一個視圖(即它倆是否是對應的,對應的表示同一個 View)

返回值:如果對應的是同一個View,返回 true,否則返回 false

上面對 PageAdapter 的四個抽象方法做了簡要說明,下面看看如何使用

簡單使用

mContentVP.setAdapter(new PagerAdapter() {@Overridepublic int getCount() {return dataList.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {View view = View.inflate(SimpleDemoActivity.this, R.layout.item_vp_demopageradapter, null);TextView pageNumTV = (TextView) view.findViewById(R.id.tv_pagenum);pageNumTV.setText("DIY-PageNum-" + dataList.get(position));container.addView(view);return view;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}});

可以看到實現(xiàn) PagerAdapter 與 BaseAdapter 很類似,只是 PagerAdapter 的 isViewFromObject() 與 instantiateItem() 方法需要好好理解下。這里為了簡化 PagerAdapter 的使用,我做了個簡單的封裝:

public abstract class APagerAdapter<T> extends PagerAdapter {protected LayoutInflater mInflater;protected List<T> mDataList;private SparseArray<View> mViewSparseArray;public APagerAdapter(Context context, List<T> dataList) {mInflater = LayoutInflater.from(context);mDataList = dataList;mViewSparseArray = new SparseArray<View>(dataList.size());}@Overridepublic int getCount() {if (mDataList == null) return 0;return mDataList.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {View view = mViewSparseArray.get(position);if (view == null) {view = getView(position);mViewSparseArray.put(position, view);}container.addView(view);return view;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView(mViewSparseArray.get(position));}public abstract View getView(int position);}

APagerAdapter 類模仿 ListView 的 BaseAdapter,抽象出一個 getView() 方法,在內(nèi)部使用 SparesArray 緩存所有顯示過的 View。這樣使用就很簡單了,繼承 APagerAdapter,實現(xiàn) getView() 方法即可(可以參考:DemoPagerAdapter.java)。

PagerAdapter 刷新的問題

在使用 ListView 的時候,我們往往習慣了更新 Adapter 的數(shù)據(jù)源,然后調(diào)用 Adapter 的 notifyDataSetChanged() 方法來刷新列表(有沒有點 MVC 的感覺)。

PagerAdapter 也有 notifyDataSetChanged() 方法,那我們按照這個流程來試試,看有沒有什么問題。(ListView 的示例就不在這里演示了,感興趣的可以自己去試試,非常簡單)

那么我的問題是:“ViewPager 的 PagerAdapter 在數(shù)據(jù)源更新后,能否自動刷新視圖?”

帶著問題,我們做一些實驗,下面實驗的思路是:修改數(shù)據(jù)源,然后通知 PagerAdapter 更新,查看視圖的變化。

實驗環(huán)境準備

看看實驗環(huán)境,上代碼:

private void initData() {// 數(shù)據(jù)源mDataList = new ArrayList<>(5);mDataList.add("Java");mDataList.add("Android");mDataList.add("C&C++");mDataList.add("OC");mDataList.add("Swift");// 很簡單的一個 PagerAdapterthis.mContentVP.setAdapter(mPagerAdapter = new PagerAdapter() {@Overridepublic int getCount() {return mDataList.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {View view = View.inflate(SimpleDemoActivity.this, R.layout.item_vp_demopageradapter, null);TextView pageNumTV = (TextView) view.findViewById(R.id.tv_pagenum);pageNumTV.setText("DIY-PageNum-" + mDataList.get(position));container.addView(view);return view;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}}); }

ViewPager 的 Item:item_vp_demopageradapter.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"android:orientation="vertical"android:gravity="center"><ImageView android:id="@+id/iv_img"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="@dimen/activity_horizontal_margin"android:src="@mipmap/ic_launcher" /><!-- 用于顯示文本,數(shù)據(jù)更新體現(xiàn)在這里 --><TextView android:id="@+id/tv_pagenum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="@dimen/activity_horizontal_margin"android:textAppearance="?android:attr/textAppearanceLarge"android:text="DIY-Page-" /> </LinearLayout>

很簡單的代碼,并且加了注釋,直接往下看實驗。

PagerAdapter 刷新實驗

1、更新數(shù)據(jù)源中的某項

對應代碼:

private void refresh() {mDataList.set(0, "更新數(shù)據(jù)源測試");mPagerAdapter.notifyDataSetChanged(); }

問題描述:在演示動畫中可以看到,更新數(shù)據(jù)源之后視圖并沒有立即刷新,多滑動幾次再次回到更新的 Item 時才更新(這里先看問題,下面會細說)。

2、往數(shù)據(jù)源中添加數(shù)據(jù)

對應代碼:

private void add() {mDataList.add("這是新添加的Item");mPagerAdapter.notifyDataSetChanged(); }

問題描述:沒什么問題,數(shù)據(jù)源添加數(shù)據(jù)后通知 PagerAdapter 刷新,ViewPager 中就多了一個 Item。

3、從數(shù)據(jù)源中刪除數(shù)據(jù)

private void delete() {mDataList.remove(0);mPagerAdapter.notifyDataSetChanged(); }

問題描述:這個問題就較多了,首先,如果是刪除當前 Item,那么會看到?jīng)]有任何反應;其次,如果刪除的不是當前 Item,會發(fā)現(xiàn)出現(xiàn)了數(shù)據(jù)錯亂,并且后面有 Item 滑不過去,但是按住往后滑的時候可以看到后面的 Item。

4、將數(shù)據(jù)源清空

private void clean() {mDataList.clear();mPagerAdapter.notifyDataSetChanged(); }

問題描述:從上面的動圖可以看到,清空數(shù)據(jù)源之后,會殘留一個 Item。

說明:先不要計較上面所寫的 PagerAdapter 是否有問題,這里只是想引出問題來,下面會針對 PagerAdapter、FragmentPagerAdapter 以及 FragmentStatePagerAdapter 來分析問題原因和給出解決方案。

二、PagerAdapter

從上面的實驗可以看出 ViewPager 不同于 ListView,如果單純的調(diào)用 ViewPager.getAdapter().notifyDataSetChanged() 方法(即 PagerAdapter 的 notifyDataSetChanged()方法)頁面并沒有刷新。

PagerAdapter 用于 ViewPager 的 Item 為普通 View的情況,這個相對簡單,所以最先介紹。

相信很多同學都搜過類似的問題 —— “PagerAdapter 的 notifyDataSetChanged() 不刷新?”。有的說這是 bug,有的則認為 Google 是特意這樣設計的,個人傾向后一種觀點(我覺得這是 Google 為了 ViewPager 性能考慮而設計的,畢竟 ViewPager 需要顯示“很多大的”視圖,而且要防止用戶滑動時覺得卡頓)。

ViewPager 刷新分析
先來了解下 ViewPager 的刷新過程:

1、刷新的起始

ViewPager 的刷新是從調(diào)用其 PagerAdapter 的 notifyDataSetChanged() 方法開始的,那先看看該方法的源碼(在源碼面前一切無所遁形…):

/*** This method should be called by the application if the data backing this adapter has changed* and associated views should update.*/ public void notifyDataSetChanged() {synchronized (this) {if (mViewPagerObserver != null) {mViewPagerObserver.onChanged();}}mObservable.notifyChanged(); }

2、DataSetObservable 的 notifyChanged()

上面的方法中出現(xiàn)了兩個關鍵的成員變量:

private final DataSetObservable mObservable = new DataSetObservable(); private DataSetObserver mViewPagerObserver;

觀察者模式,有沒有?先不著急分析這個是不是觀察者模式,來看看 mObservable.notifyChanged() 做了些什么工作:

/*** Invokes {@link DataSetObserver#onChanged} on each observer.* Called when the contents of the data set have changed. The recipient* will obtain the new contents the next time it queries the data set.*/ public void notifyChanged() {synchronized(mObservers) {// since onChanged() is implemented by the app, it could do anything, including// removing itself from {@link mObservers} - and that could cause problems if// an iterator is used on the ArrayList {@link mObservers}.// to avoid such problems, just march thru the list in the reverse order.for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onChanged(); }} }

notifyChanged() 方法中是很典型的觀察者模式中遍歷所有的 Observer,通知 變化發(fā)生了的代碼。代碼很簡單,那關鍵是這個 mObservers 包含哪些 Observer 呢?

3、DataSetObserver

直接從 mObservers 點進去你會發(fā)現(xiàn)這個:

protected final ArrayList<T> mObservers = new ArrayList<T>();

-_-‘,這是個泛型,坑了!還好 DataSetObservable 的 notifyChanged() 的注釋中寫了這些 Observer 是 DataSetObserver。那去看看 DataSetObserver:

public abstract class DataSetObserver {/*** This method is called when the entire data set has changed,* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.*/public void onChanged() {// Do nothing}/*** This method is called when the entire data becomes invalid,* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a* {@link Cursor}.*/public void onInvalidated() {// Do nothing} }

一個抽象類,里面兩個空方法,這個好辦,找他的子類(AndroidStudio 中 將光標放到類名上,按 F4):

總算找到你了,就是用紅線框出來的那條,雙擊,定位過去。

4、PagerObserver 內(nèi)部類

PagerObserver 是 ViewPager 中的一個內(nèi)部類,實現(xiàn)也很簡單,就是調(diào)用了 ViewPager 中的 dataSetChanged() 方法,真正的關鍵來了。

private class PagerObserver extends DataSetObserver {@Overridepublic void onChanged() {dataSetChanged();}@Overridepublic void onInvalidated() {dataSetChanged();} }

5、ViewPager 的 dataSetChanged()

這個方法的實現(xiàn)較長,里面的邏輯看上去挺復雜的,這里就不展示全部的源碼了,列下關鍵點:

... for (int i = 0; i < mItems.size(); i++) {final ItemInfo ii = mItems.get(i);final int newPos = mAdapter.getItemPosition(ii.object);if (newPos == PagerAdapter.POSITION_UNCHANGED) {continue;}if (newPos == PagerAdapter.POSITION_NONE) {...continue;}... } ...

上面截取的代碼中 for 循環(huán)里面有兩個 continue 語句,這可能是比較關鍵的代碼,幸好不用我們繼續(xù)深入了,官方給出了解釋:

Called when the host view is attempting to determine if an item’s position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.

大致的意思是:
如果 Item 的位置如果沒有發(fā)生變化,則返回 POSITION_UNCHANGED。如果返回了 POSITION_NONE,表示該位置的 Item 已經(jīng)不存在了。默認的實現(xiàn)是假設 Item 的位置永遠不會發(fā)生變化,而返回 POSITION_UNCHANGED。(參考自:追溯源碼解決android疑難有關問題1-Viewpager之notifyDataSetChanged無刷新)

上面在源碼里面跟了一大圈是不是還是感覺沒有明朗,因為還有一個很關鍵的類 —— PagerAdapter 沒有介紹,再給點耐心,繼續(xù)。

6、PagerAdapter 的工作流程

其實就是 PagerAdapter 中方法的執(zhí)行順序,來看看 Leo8573 的分析(個人感覺基本說到位了,所以直接拷過來了):

PagerAdapter 作為 ViewPager 的適配器,無論 ViewPager 有多少頁,PagerAdapter 在初始化時也只初始化開始的2個 View,即調(diào)用2次instantiateItem 方法。而接下來每當 ViewPager 滑動時,PagerAdapter 都會調(diào)用 destroyItem 方法將距離該頁2個步幅以上的那個 View 銷毀,以此保證 PagerAdapter 最多只管轄3個 View,且當前 View 是3個中的中間一個,如果當前 View 缺少兩邊的 View,那么就 instantiateItem,如里有超過2個步幅的就 destroyItem。

簡易圖示:

*

——+—+—+—+——
… 0 | 1 | 2 | 3 | 4 …
——+—+—+—+——
當前 View 為2號 View,所以 PagerAdapter 管轄1、2、3三個 View,接下來向左滑動–>

*

——+—+—+—+——
… 1 | 2 | 3 | 4 | 5 …
——+—+—+—+——
滑動后,當前 View 變?yōu)?號 View,PagerAdapter 會 destroyItem 0號View,instantiateItem 5號 View,所以 PagerAdapter 管轄2、3、4三個 View。(參考自:關于ViewPager的數(shù)據(jù)更新問題小結)

總結一下: Viewpager 的刷新過程是這樣的,在每次調(diào)用 PagerAdapter 的 notifyDataSetChanged() 方法時,都會激活 getItemPosition(Object object) 方法,該方法會遍歷 ViewPager 的所有 Item(由緩存的 Item 數(shù)量決定,默認為當前頁和其左右加起來共3頁,這個可以自行設定,但是至少會緩存2頁),為每個 Item 返回一個狀態(tài)值(POSITION_NONE/POSITION_UNCHANGED),如果是 POSITION_NONE,那么該 Item 會被 destroyItem(ViewGroup container, int position, Object object) 方法 remove 掉,然后重新加載,如果是 POSITION_UNCHANGED,就不會重新加載,默認是 POSITION_UNCHANGED,所以如果不重寫 getItemPosition(Object object),修改返回值,就無法看到 notifyDataSetChanged() 的刷新效果。

最簡單的解決方案
那就是直接一刀切:重寫 PagerAdapter 的 getItemPosition(Object object) 方法,將返回值固定為 POSITION_NONE。

先看看效果:

上代碼(PagerAdapterActivity.java):

@Override public int getItemPosition(Object object) {// 最簡單解決 notifyDataSetChanged() 頁面不刷新問題的方法return POSITION_NONE; }

該方案的缺點:有個很明顯的缺陷,那就是會刷新所有的 Item,這將導致系統(tǒng)資源的浪費,所以這種方式不適合數(shù)據(jù)量較大的場景。

注意:
這種方式還有一個需要注意的地方,就是重寫 destoryItem() 方法:

@Override public void destroyItem(ViewGroup container, int position, Object object) {// 把 Object 強轉為 View,然后將 view 從 ViewGroup 中清除container.removeView((View) object); }

最簡方案的優(yōu)化
這里提供一個思路,畢竟場景太多,相信大家理解了思路要實現(xiàn)就很簡單了,閑話不多說。

思路:在 instantiateItem() 方法中給每個 View 添加 tag(使用 setTag() 方法),然后在 getItemPosition() 方法中通過 View.getTag() 來判斷是否是需要刷新的頁面,是就返回 POSITION_NONE,否就返回 POSITION_UNCHANGED。 (參考自:ViewPager刷新單個頁面的方法)

注意:這里有一點要注意的是,當清空數(shù)據(jù)源的時候需要返回 POSITION_NONE,可用如下代碼:

if (mDataList != null && mDataList.size()==0) {return POSITION_NONE; }

關于 PagerAdapter 的介紹就到這里了,雖然 FragmentPagerAdapter 與 FragmentStatePagerAdapter 都是繼承自 PagerAdapter。但是,這兩個是專門為以 Fragment 為 Item 的 ViewPager 所準備的,所以有其特殊性。且看下面的介紹。

三、FragmentPagerAdapter

簡介
上面通過使 getItemPosition() 方法返回 POSITION_NONE 到達數(shù)據(jù)源變化(也就是調(diào)用 notifyDataSetChanged())時,刷新視圖的目的。但是當我們使用 Fragment 作為 ViewPager 的 Item 時,就需要多考慮一些了,而且一般是使用 FragmentPagerAdapter 或者 FragmentStatePagerAdapter。

這里不展開討論 FragmentPagerAdapter 與 FragmentStatePagerAdapter 的異同和使用場景了,感興趣的可以看看這篇文章:FragmentPagerAdapter與FragmentStatePagerAdapter區(qū)別。

下面先來看看使用 FragmentPagerAdapter 時,如何在數(shù)據(jù)源發(fā)生變化時,刷新 Fragment 或者動態(tài)改變 Items 的數(shù)量。

方案:清除 FragmentManager 中緩存的 Fragment
先看效果:

實現(xiàn)上圖效果的關鍵代碼:

1、FPagerAdapter1Activity.java

private void refresh() {if (checkData()) return;mDataList.set(0, 7); // 修改數(shù)據(jù)源mPagerAdapter.updateData(mDataList); // 通知 Adapter 更新 }private void add() {mDataList.add(7);mPagerAdapter.updateData(mDataList); }private void delete() {if (checkData()) return;mDataList.remove(0);mPagerAdapter.updateData(mDataList); }private void clear() {if (checkData()) return;mDataList.clear();mPagerAdapter.updateData(mDataList); }

2、FPagerAdapter1.java

public class FPagerAdapter1 extends FragmentPagerAdapter {private ArrayList<Fragment> mFragmentList;private FragmentManager mFragmentManager;public FPagerAdapter1(FragmentManager fm, List<Integer> types) {super(fm);this.mFragmentManager = fm;mFragmentList = new ArrayList<>();for (int i = 0, size = types.size(); i < size; i++) {mFragmentList.add(FragmentTest.instance(i));}setFragments(mFragmentList);}public void updateData(List<Integer> dataList) {ArrayList<Fragment> fragments = new ArrayList<>();for (int i = 0, size = dataList.size(); i < size; i++) {Log.e("FPagerAdapter1", dataList.get(i).toString());fragments.add(FragmentTest.instance(dataList.get(i)));}setFragments(fragments);}private void setFragments(ArrayList<Fragment> mFragmentList) {if(this.mFragmentList != null){FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();for(Fragment f:this.mFragmentList){fragmentTransaction.remove(f);}fragmentTransaction.commit();mFragmentManager.executePendingTransactions();}this.mFragmentList = mFragmentList;notifyDataSetChanged();}@Overridepublic int getCount() {return this.mFragmentList.size();}public int getItemPosition(Object object) {return POSITION_NONE;}@Overridepublic Fragment getItem(int position) {return mFragmentList.get(position);} }

3、思路分析
上面的代碼思路很簡單,就是當數(shù)據(jù)源發(fā)生變化時,先將 FragmentManger 里面所有緩存的 Fragment 全部清除,然后重新創(chuàng)建,這樣達到刷新視圖的目的。

但是,這樣做有一個缺點,那就是會造成不必要的浪費,會影響性能。還有就是必須使用一個 List 緩存所有的 Fragment,這也得占用不少內(nèi)存…

思路挺簡單,這里不再贅述,那看看有沒有什么可以優(yōu)化的。

優(yōu)化:通過 Tag 獲取緩存的 Fragment
先看效果:

從上面的動圖上可以看到,更新某一個 Fragment 沒有問題,清空數(shù)據(jù)源的時候也沒有,添加當然也沒什么問題;請注意刪除的效果,雖然,目的 Fragment 確實從 ViewPager 中移除了,但是滑動后面的頁面會發(fā)現(xiàn)出現(xiàn)了數(shù)據(jù)錯亂。

分析一下優(yōu)化的思路:

先來了解 FragmentPagerAdapter 中是如何管理 Fragment 的,這里涉及到 FragmentPagerAdapter 中的 instantiateItem() 方法:

@Override public Object instantiateItem(ViewGroup container, int position) {if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();}final long itemId = getItemId(position);// Do we already have this fragment?String name = makeFragmentName(container.getId(), itemId);Fragment fragment = mFragmentManager.findFragmentByTag(name);if (fragment != null) {if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);mCurTransaction.attach(fragment);} else {fragment = getItem(position);if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);mCurTransaction.add(container.getId(), fragment,makeFragmentName(container.getId(), itemId));}if (fragment != mCurrentPrimaryItem) {fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);}return fragment; }

從源碼中可以看到在從 FragmentManager 中取出 Fragment 時調(diào)用了 findFragmentByTag() 方法,而這個 Tag 是由 makeFragmentName() 方法生成的。繼續(xù)往下可以看到每一個 Fragment 都打上了一個標簽(在 mCurTransaction.add() 方法中)。

也就是說是 FragmentManager 通過 Tag 找相應的 Fragment,從而達到緩存 Fragment 的目的。如果可以找到,就不會創(chuàng)建新的 Fragment,Fragment 的 onCreate()、onCreateView() 等方法都不會再次調(diào)用。

那優(yōu)化的思路就有了:
首先,需要緩存所有 Fragment 的 Tag,代碼如下:

private List<String> mTagList; // 用來存放所有的 Tag// 生成 Tag // 直接從 FragmentPageAdapter 源碼里拷貝 Fragment 生成 Tag 的方法 private String makeFragmentName(int viewId, int index) {return "android:switcher:" + viewId + ":" + index; }// 將 Tag 緩存到 List 中 @Override public Object instantiateItem(ViewGroup container, int position) {mTagList.add(position, makeFragmentName(container.getId(),(int) getItemId(position)));return super.instantiateItem(container, position); } 其次,在更新 Fragment 時,使用相應的 Tag 去 FragmentManamager 中找相應的 Fragment,如果存在,就直接更新,代碼如下:public void update(int position, String str) {Fragment fragment = mFragmentManager.findFragmentByTag(mTagList.get(position));if (fragment == null) return;if (fragment instanceof FragmentTest) {((FragmentTest)fragment).update(str);}notifyDataSetChanged(); }

該方法需要自行在 Fragment 中提供。

最后,對于動態(tài)改變 ViewPager 中 Fragment 的數(shù)量,如果是添加,那沒什么要注意的;但是刪除有點棘手。

在上面的動態(tài)上看到,刪除一個 Fragment 后會出現(xiàn)混亂,這里沒有進一步去研究了,這里僅提供一個示例供參考(這個示例代碼有問題,僅供參考)

public void remove(int position) {mDataList.remove(position);isDataSetChange = true;Fragment fragment = mFragmentManager.findFragmentByTag(mTagList.get(position));mTagList.remove(position);if (fragment == null) {notifyDataSetChanged();return;}FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();fragmentTransaction.remove(fragment);fragmentTransaction.commit();mFragmentManager.executePendingTransactions();notifyDataSetChanged(); }

注意:
這個”優(yōu)化“示例,僅僅適用于在只需要更新某個 Fragment 的場景,關于動態(tài)刪除 Fragment,該”優(yōu)化“方案并不適用,也不推薦使用。

四、FragmentStatePagerAdapter

先看效果:

簡介

FragmentStatePagerAdapter 與 FragmentPagerAdapter 類似,這兩個類都繼承自 PagerAdapter。但是,和 FragmentPagerAdapter 不一樣的是,FragmentStatePagerAdapter 只保留當前頁面,當頁面離開視線后,就會被消除,釋放其資源;而在頁面需要顯示時,生成新的頁面(這和 ListView 的實現(xiàn)一樣)。這種方式的好處就是當擁有大量的頁面時,不必在內(nèi)存中占用大量的內(nèi)存。(參考自:FragmentPagerAdapter與FragmentStatePagerAdapter區(qū)別)

FragmentStatePagerAdapter 的實現(xiàn)與 FragmentPagerAdapter 有很大區(qū)別,如果照搬上述 FragmentPagerAdapter 刷新數(shù)據(jù)的方式,你會發(fā)現(xiàn)沒有什么問題(可以使用 FPagerAdapter11.java 測試)。

另一種思路
但是,我在項目中實際應用的時候(Fragment 比較復雜,里面有網(wǎng)絡任務等)出現(xiàn)了 IllegalStateException,發(fā)生在 ”fragmentTransaction.remove(f);“ 時。當時找了一些文章沒有解決該問題,考慮到項目中的 Fragment 里面邏輯過多,就換思路,沒有在這個上面繼續(xù)深究了。

如果,你也是這樣使用 FragmentStatePagerAdapter 來動態(tài)改變 ViewPager 中 Fragment,并且在 remove Fragment 時遇到了 IllegalStateException。那么,你可以考慮使用下面的方式,先看代碼(FSPagerAdapter .java):

public class FSPagerAdapter extends FragmentStatePagerAdapter {private ArrayList<Fragment> mFragmentList;public FSPagerAdapter(FragmentManager fm, List<Integer> types) {super(fm);updateData(types);}public void updateData(List<Integer> dataList) {ArrayList<Fragment> fragments = new ArrayList<>();for (int i = 0, size = dataList.size(); i < size; i++) {Log.e("FPagerAdapter1", dataList.get(i).toString());fragments.add(FragmentTest.instance(dataList.get(i)));}setFragmentList(fragments);}private void setFragmentList(ArrayList<Fragment> fragmentList) {if(this.mFragmentList != null){mFragmentList.clear();}this.mFragmentList = fragmentList;notifyDataSetChanged();}@Overridepublic int getCount() {return this.mFragmentList.size();}public int getItemPosition(Object object) {return POSITION_NONE;}@Overridepublic Fragment getItem(int position) {return mFragmentList.get(position);} }

對應的測試 Activity 見 FSPagerAdapterActivity.java。

上面的代碼挺簡單,稍微解釋一下實現(xiàn)思路:
1、緩存所有的 Fragment
使用一個 List 將數(shù)據(jù)源對應的 Fragment 都緩存起來

2、更新數(shù)據(jù)源,刷新 Fragment
當有數(shù)據(jù)源更新的時候,從 List 中取出相應的 Fragment,然后刷新 Adapter

3、刪除數(shù)據(jù)時,刪除 List 中對應的 Fragment
當數(shù)據(jù)源中刪除某項時,將 List 中對應的 Fragment 也刪除,然后刷新 Adapter

小結
關于 ViewPager 數(shù)據(jù)源刷新比較麻煩的地方是從數(shù)據(jù)源中刪除數(shù)據(jù)的情況,這和 ViewPager 的實現(xiàn)方式有關,我們在解決該問題的時候要分具體情況來采取不同的方案。

上面提供的方案也不是完美的,還有很多不足,如果你在應用的過程中遇到了問題,那么請反饋給我,大家一起完善。

這里主要是探討關于 ViewPager 數(shù)據(jù)源刷新的問題,關于 ViewPager 的詳細使用不是本文重點,這里就不涉及了。

總結

以上是生活随笔為你收集整理的ViewPager刷新问题详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。