调用了PagerAdapter.notifyDataSetChanged() 后,ViewPager没有刷新

目录

1. 问题:

2. 结论

3. 源码追踪分析

(1) PagerAdapter.notifyDataSetChanged() 的源码:

(2) ViewPager.setAdapter 源码

(3) ViewPager.PagerObserver 源码

(4)  PagerAdapter.getItemPosition() 源码

(5)ViewPager. dataSetChanged() 源码


1. 问题:

每一个ViewPager实例对象 都会设置一个PagerAdapter 的实例对象.

当数据改变 时,需要调用PagerAdapter.notifyDataSetChanged() 去刷新界面 (即ViewPager).

但是问题来了: 即使调用了notifyDataSetChanged() , 界面也没有刷新,即 PagerAdapter.instantiateItem() 没有被回调。

2. 结论

解决方法是重写 PagerAdapter.getItemPosition() 方法, 并返回 PagerAdapter.POSITION_NONE

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

3. 源码追踪分析

原因:

(1) PagerAdapter.notifyDataSetChanged() 的源码:

public abstract class PagerAdapter {
    /**
     * 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();
    }

    void setViewPagerObserver(DataSetObserver observer) {
        synchronized (this) {
            mViewPagerObserver = observer;
        }
    }
}

可以看到,最终会调用 mViewPagerObserver.onChanged()。 那么这个onChanged 是在哪里实现的呢???

我们可以看到 mViewPagerObserver 是通过 setViewPagerObserver(DataSetObserver observer) 进行设置的,那么需要看它是在哪里被调用的。

回顾我们在设置adapter时有如下代码:

(2) ViewPager.setAdapter 源码

public class ViewPager extends ViewGroup {

    /**
     * Set a PagerAdapter that will supply views for this pager as needed.
     *
     * @param adapter Adapter to use
     */
    public void setAdapter(@Nullable PagerAdapter adapter) {
        //....省略其它
        final PagerAdapter oldAdapter = mAdapter;
        mAdapter = adapter;
        mExpectedAdapterCount = 0;

        if (mAdapter != null) {
            if (mObserver == null) {
                mObserver = new PagerObserver();
            }
            mAdapter.setViewPagerObserver(mObserver);

}

这里的Adapter.setViewPagerObserver(mObserver)  设置 mViewPagerObserver 的地方。

只不过这里传递参数的是ViewPager 的内部类 PagerObserver 实例对象,而它实现了 DataSetObserver 接口,所以这就对应得上了。

(3) ViewPager.PagerObserver 源码

    private class PagerObserver extends DataSetObserver {
        PagerObserver() {
        }

        @Override
        public void onChanged() {
            dataSetChanged();
        }
        @Override
        public void onInvalidated() {
            dataSetChanged();
        }
    }

所以,最终的逻辑处理还是在 ViewPager 的方法 dataSetChanged()  (放在文章最后,先说明逻辑)

它又会根据 getItemPosition()的返回值(当前的页面) 进行处理:

  1.   PagerAdapter.POSITION_UNCHANGED (值为-1),  则不会移除旧的页面和 加载新的页面。
  2.   PagerAdapter.POSITION_NONE (值为0), 会移除旧的页面和 加载新的页面。

而PagerAdapter的默认实现则是返回POSITION_UNCHANGED

(4)  PagerAdapter.getItemPosition() 源码

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

因此,我们需要重写这个函数,并且返回 PagerAdapter.POSITION_NONE (值为0)

(5)ViewPager. dataSetChanged() 源码

需要注意变量 newPos 、needPopulate

    void dataSetChanged() {
        // This method only gets called if our observer is attached, so mAdapter is non-null.

        final int adapterCount = mAdapter.getCount();
        mExpectedAdapterCount = adapterCount;
        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
                && mItems.size() < adapterCount;
        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, adapterCount - 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();
        }
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值