一、問題來由 ViewPager控制項很大程度上滿足了開發者開發頁面左右移動切換的功能,使用非常方便。但是使用中發現,在刪除或者修改數據的時候,PagerAdapter無法像BaseAdapter那樣僅通過notifyDataSetChanged方法通知刷新View。有人提出一種解決方案:給Viewp ...
一、問題來由
ViewPager控制項很大程度上滿足了開發者開發頁面左右移動切換的功能,使用非常方便。但是使用中發現,在刪除或者修改數據的時候,PagerAdapter無法像BaseAdapter那樣僅通過notifyDataSetChanged方法通知刷新View。有人提出一種解決方案:給Viewpager重新設置一遍適配器adapter,來達到刷新數據的目的。但是這種方法在大多數情況下,是存在問題的。
二、問題分析
為什麼調用數據更新的方法,Viewpager卻沒有更新呢,我們跟進該方法的源代碼看一下。
/** * This method should be called by the application if the data backing this adapter has changed * and associated views should update. */ public void notifyDataSetChanged() { mObservable.notifyChanged(); }
註釋里說到,當附加在適配器上的數據發生變化時,應該調用該方法刷新數據。該方法調用了一個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(); } } }
這都不是重點,重點我們來看這個mObservers的類型是一個抽象類DataSetObserver,裡面只有兩個未實現的方法,都有誰使用了這個抽象類呢,快捷鍵 ctrl + alt + H ,在眾多的調用者當中,我們發現了Viewpager的身影。進入Viewpager,我們終於找到了Viewpager中控制數據變更的重點方法dataSetChanged ,這個方法如下:
void dataSetChanged () { // This method only gets called if our observer is attached, so mAdapter is non-null. boolean needPopulate = mItems .size() < mOffscreenPageLimit * 2 + 1 && mItems.size() < mAdapter.getCount(); int newCurrItem = mCurItem ; boolean isUpdating = false; 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) { mItems.remove(i); i--; if (!isUpdating) { mAdapter.startUpdate( this); isUpdating = true; } mAdapter.destroyItem( this, ii.position , ii.object); needPopulate = true; if (mCurItem == ii.position ) { // Keep the current item in the valid range newCurrItem = Math. max(0, Math.min(mCurItem, mAdapter.getCount() - 1)); needPopulate = true; } continue; } if (ii.position != newPos) { if (ii.position == mCurItem ) { // Our current item changed position. Follow it. newCurrItem = newPos; } ii. position = newPos; needPopulate = true; } } if (isUpdating) { mAdapter.finishUpdate( this); } Collections. sort(mItems, COMPARATOR); if (needPopulate) { // Reset our known page widths; populate will recompute them. final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.isDecor ) { lp. widthFactor = 0.f; } } setCurrentItemInternal(newCurrItem, false, true); requestLayout(); } }
重點看這樣一行代碼:
final int newPos = mAdapter.getItemPosition(ii.object); if (newPos == PagerAdapter.POSITION_UNCHANGED) { continue ; }
到這裡我們就找到瞭解決這個問題的核心方法:getItemPosition()。官方對getItemPosition()的解釋是:
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 orPOSITION_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的位置如果沒有發生變化,則返回POSITION_UNCHANGED。如果返回了POSITION_NONE,表示該位置的item已經不存在了。預設的實現是假設item的位置永遠不會發生變化,而返回POSITION_UNCHANGED
三、解決方案
根據上面的分析,我們可以嘗試著修改適配器的寫法,覆蓋getItemPosition()方法,當調用notifyDataSetChanged時,讓getItemPosition方法人為的返回POSITION_NONE,從而達到強制Viewpager重繪所有item的目的。
@Override public int getItemPosition(Object object) { return POSITION_NONE; }
參考資料:https://www.cnblogs.com/cheneasternsun/p/6017012.html