ViewPager的PagerAdapter中的notifyDataSetChanged更新数据总结

最近在工作中遇到了一个问题,就是在viewpager中调用pageradapter.notifydatasetchanged方法,好像没有任何效果,相应的view也没有更新数据,根据官方API是这样解释的:大概是说明Adapter会自动管辖ViewPager每一页(Item)的状态,而notifyDataSetChanged()是用在当ViePager要新增一页、刪除一页或改变各个页面的排列的時候。所以ViewPager Adapter的notifyDataSetChanged自然就不适用于只更新View Pager里面某个View的內容的需求。

通常的解决方案是重写 PagerAdapter 的 getItemPosition() 方法,并返回 POSITION_NONE ,以触发 PagerAdapter 销毁并重建对象。

下面我们通过源代码分析为什么必须 override这个方法。

首先看 PagerAdapter 源代码,它的 notifiDataSetChanged() 方法内部调用了一个 DataSetObservable 对象的 notifyChanged() 方法:

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

跟进这个方法,我们找到DataSetObservable的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,里面只有两个为实现的方法:

/**
 * Receives call backs when a data set has been changed, or made invalid. The typically data sets
 * that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
 * DataSetObserver must be implemented by objects which are added to a DataSetObservable.
 */
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
    }
}

都有谁使用了这个抽象类呢,ctrl+T可以查看调用者

ViewPager的PagerAdapter中的notifyDataSetChanged更新数据总结_第1张图片

进入viewpager,找到控制数据更新的重点代码:

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);
//根据getItemPosition的返回值,来决定是否调用destroyItem方法。
            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();
        }
    }

从上面我们看到,它会判断adapter的getItemPosition方法的返回值,只有当返回值是POSITION_NONE时候,才会调用item的remove方法以及startUpdate和destroyItem方法,进而去更新数据。默认返回值是POSITION_UNCHANGED,不执行任何操作。

PagerAdapter 的 getItemPosition() 方法的默认实现:

public int getItemPosition(Object object) {
  return POSITION_UNCHANGED;
}

看到这里,大家基本就可以理解为什么我们要覆盖这个方法了。这个方法很简单,但是每次调用adapter都会去调用destroy方法,因此最好是自己做view的缓存,destroy方法可以默认不处理,下面贴上简单实现:

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

@Override
        public Object instantiateItem(View container, int position) {
//下面是我自己定义的View对象,也可以用layoutinflat xml文件的方法
            CustomerView view = viewList[position];
            ViewGroup viewpager = ((ViewGroup) container);
            if(view == null) {
                view = new CustomerView (mContext);
                viewList[position] = view;
                (viewpager).addView(view, 0);
            }
             return view;
        }
        
        @Override
        public void destroyItem(View container, int position,
                Object object) {
container.removeView(viewList[position]);
                   }

参考:http://rincliu.com/blog/2013/09/29/viewpager/

你可能感兴趣的:(ViewPager的PagerAdapter中的notifyDataSetChanged更新数据总结)