FragmentStatePagerAdapter虽然过时,但是老项目使用,问题也不大。
背景:首页tab+viewPager会根据用户偏好来改变展示顺序,但是当用户改变偏好后,调用fragmentStatePagerAdapter.nitifyDataSetChanged()时,发现刷新有时不生效,所以看源码
直接去ViewPager的dataSetChanged(),函数,为什么直接看这个?(自己看nitifyDataSetChanged()的实现)
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(); } }
关键是newPos的获取和newPos的判断,发现只有newPos == POSITION_NONE的时候,item才会改变,所以看mAdapter.getItemPosition的定义
/** * Called when the host view is attempting to determine if an item's position * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given * item has not changed or {@link #POSITION_NONE} if the item is no longer present * in the adapter. * * <p>The default implementation assumes that items will never * change position and always returns {@link #POSITION_UNCHANGED}. * * @param object Object representing an item, previously returned by a call to * {@link #instantiateItem(View, int)}. * @return object's new position index from [0, {@link #getCount()}), * {@link #POSITION_UNCHANGED} if the object's position has not changed, * or {@link #POSITION_NONE} if the item is no longer present. */ public int getItemPosition(@NonNull Object object) { return POSITION_UNCHANGED; }
空实现,但是注释很明白,当item的position没发生变化的时候返回POSITION_UNCHANGED,发生变化时返回POSITION_NONE,结合上边的newPos的判断逻辑,我们可以重写getItemPosition方法
class HomePageAdapter(var data: List<BaseNavigationFragment>, fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) { var oldCount = data.size var changeSort = false set(value) { field = value oldCount = data.size } override fun getItem(position: Int): Fragment = data[position] override fun getItemPosition(item: Any): Int { if (changeSort && oldCount > 0) { oldCount-- return POSITION_NONE } return POSITION_UNCHANGED } override fun getCount(): Int = data.size }
顺序发生变化时直接返回POSITION_NONE,为了不影响其他正常流程中的逻辑,此处设置了oldCount,当变化发生后,所有的item都走一遍之后,就返回POSITION_UNCHANGED(因为我们的业务逻辑中只有一处会发生顺序变化!)