ViewPager調用notifyDataSetChanged() 刷新問題解決方案

1、問題來由

ViewPager控件很大程度上知足了開發者開發頁面左右移動切換的功能,使用很是方便。可是使用中發現,在刪除或者修改數據的時候,PagerAdapter沒法像BaseAdapter那樣僅經過notifyDataSetChanged方法通知刷新View。有人提出一種解決方案:給Viewpager從新設置一遍適配器adapter,來達到刷新數據的目的。可是這種方法在大多數狀況下,是存在問題的。html

2、問題分析

爲何調用數據更新的方法,Viewpager卻沒有更新呢,咱們跟進該方法的源代碼看一下。app

/**
 * 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();ide

/**
 * 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 ,這個方法以下:this

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

  重點看這樣一行代碼:spa

final int newPos = mAdapter.getItemPosition(ii.object);
if (newPos == PagerAdapter.POSITION_UNCHANGED) {
      continue ;
}

到這裏咱們就找到了解決這個問題的核心方法:getItemPosition()。官方對getItemPosition()的解釋是:code

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.server

The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.htm

意思是若是item的位置若是沒有發生變化,則返回POSITION_UNCHANGED。若是返回了POSITION_NONE,表示該位置的item已經不存在了。默認的實現是假設item的位置永遠不會發生變化,而返回POSITION_UNCHANGEDblog

3、解決方案

根據上面的分析,咱們能夠嘗試着修改適配器的寫法,覆蓋getItemPosition()方法,當調用notifyDataSetChanged時,讓getItemPosition方法人爲的返回POSITION_NONE,從而達到強制Viewpager重繪全部item的目的。ip

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

 

 

 

 

參考資料:http://www.javashuo.com/article/p-kbpmuqna-hd.html

相關文章
相關標籤/搜索