ViewPager与ViewPager2的Adapter对比

1. ViewPagerAdapter与其两个子类FragmentPagerAdapter、FragmentStatePagerAdapter

1.1. ViewPagerAdapter重要方法剖析

a. instantiateItem(ViewGroup container, int position)

该方法的作用是根据position创建页面,container用于承载被添加的View。

b. setPrimaryItem(ViewGroup container, int position, Object object)

该方法的作用是通知ViewPagerAdapter当前的主page,即展示给用户的当前页面。

c. startUpdate(@NonNull View container)/finishUpdate(@NonNull ViewGroup container)

这两个方法总是成对被调用的,调用时机是展示页面发生变化时。

d. destroyItem(@NonNull View container, int position, @NonNull Object object)

这个方法的调用时机是ViewPager从其类型为ArrayList的成员变量mItems中remove一个对象时,然后通过调用ViewPagerAdapter的destroyItem()方法将container中的View删除。
总结一下:以上几个方法基本上都是在ViewPager中的populate()方法中被调用的,其时序为:

ViewPager ViewPagerAdapter populate() 以下函数均是在populate()中被调用 startUpdate() addNewItem() instantiateItem() 此处调用destroyItem()跟mOffscreenPageLimit有关 destroyItem() setPrimaryItem() finishUpdate() ViewPager ViewPagerAdapter

1.2. FragmentPagerAdapter剖析

该适配器继承自PagerAdapter,主要实现了每个页面是Fragment情况的适配工作,内部通过FragmentManager来管理各个Fragment。只要用户还可以返回到某个页面,该Fragment就会永久保留在片段管理器中。主要工作有两个:

a. 重写instantiateItem()方法,通过mFragmentManager来创建Fragment

public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
        	//如果mFragmentManager中已经存在当前Fragment,则开启一个 ATTATCH事务
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
        	//如果mFragmentManager中不存在当前Fragmen,则需要创建一个Fragment,并开启一个ADD事务
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        //如果此Fragment不是主Fragm,需根据mBehavior来对Fragment的生命周期降级
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
            	//最多会执行到onStart()回调
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
            } else {
            	//采用原来的setUserVisibleHint(false)处理方式,//注意此处传入参数为false
                fragment.setUserVisibleHint(false);
            }
        }
        return fragment;
    }

b. 重写setPrimaryItem()方法,设置主Fragment的生命周期为RESUMED,将即将隐藏的原主Fragment的生命周期降级为STARTED

public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment)object;
        //
        if (fragment != mCurrentPrimaryItem) {
        	//当mCurrentPrimaryItem != null时,原主Fragment即将隐藏
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                } else {
                    mCurrentPrimaryItem.setUserVisibleHint(false);
                }
            }
            fragment.setMenuVisibility(true);
            //将主Fragement的生命周期设置为RESUMED
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                if (mCurTransaction == null) {
                    mCurTransaction = mFragmentManager.beginTransaction();
                }
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
            	//注意此处传入参数为true
                fragment.setUserVisibleHint(true);
            }

            mCurrentPrimaryItem = fragment;
        }
    }

1.3. FragmentStatePagerAdapter剖析

该适配器也继承自PagerAdapter,其功能和FragmentPagerAdapter类似,主要也是重写instantiateItem()方法和setPrimaryItem()方法通过FagmentManager来管理Fragment。与其不同的是FragmentStatePagerAdapter还是实现了Fragment的缓存功能,通过类型为ArrayListy的成员变量mSavedState和mFragments来实现。

a. instantiateItem()方法的不同实现

public Object instantiateItem(@NonNull ViewGroup container, int position) {
        //如果能从mFragments缓存中拿到该position对应的Fragment立即返回该Fagement
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
		//如果mFragments缓存中不存在该position对应的Fragment,则通过getItem()创建一个Fragment
        Fragment fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
        	//如果mSavedState缓存中存在该position对应的SavedState对象,则将该对象赋值给新创建的Fragment的mSavedFragmentState变量,
        	//用于该新Fragment恢复原来的数据。
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        //原处理方式setUserVisibleHint(false)
        if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
            fragment.setUserVisibleHint(false);
        }
		//	将当前Fragment缓存在mFragments列表中
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

        if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
            mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
        }

        return fragment;
    }

b. setPrimaryItem()的实现与FragmentPagerAdapter类中的实现是相同的

c. 重写saveState()

ViewPager重写了View的onSaveInstanceState()方法,在该方法中又会调用FragmentStatePagerAdapter的saveState()来将mSavedState与mFragments存储在Bundle对象中。

public Parcelable saveState() {
        Bundle state = null;
        //存储mSavedState
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        //存储mFragments
        for (int i=0; i<mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }

d. 重写restoreState()方法

ViewPager重写了View的onRestoreInstanceState()方法,在该方法中又会调用FragmentStatePagerAdapter的restoreState()方法来恢复存储在Bundle中的mSavedState与mFragments对象。代码逻辑与saveState()是相对应的。

2. FragmentStateAdapter

FragmentStateAdapter继承自RecyclerView.Adapter,下面看一下关键方法的实现。

a. 重写onCreateViewHolder()

返回一个FragmentViewHolder对象,该FragmentViewHolder的根布局为FrameLayout,即itemView为FrameLayout对象。

b. 重写onBindViewHolder()

重写该方法主要做三件事:

  1. 更新mItemIdToViewHolder
  2. 创建Fragment
  3. 垃圾回收无用Fragment
public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
        final long itemId = holder.getItemId();
        final int viewHolderId = holder.getContainer().getId();
        final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
        if (boundItemId != null && boundItemId != itemId) {
            removeFragment(boundItemId);
            mItemIdToViewHolder.remove(boundItemId);
        }
		//1. 更新mItemIdToViewHolder
        mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
        //2. 创建Fragment
        ensureFragment(position);

        /** Special case when {@link RecyclerView} decides to keep the {@link container}
         * attached to the window, but not to the view hierarchy (i.e. parent is null) */
        final FrameLayout container = holder.getContainer();
        if (ViewCompat.isAttachedToWindow(container)) {
            if (container.getParent() != null) {
                throw new IllegalStateException("Design assumption violated.");
            }
            container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom,
                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    if (container.getParent() != null) {
                        container.removeOnLayoutChangeListener(this);
                        placeFragmentInViewHolder(holder);
                    }
                }
            });
        }
		//3. 垃圾回收无用Fragment
        gcFragments();
    }

在ensureFragment()方法中会调用createFragment()虚方法来创建Fragment,需要通过子类实现自己的创建逻辑:

private void ensureFragment(int position) {
        long itemId = getItemId(position);
        //当没有position对应的Fragment时才创建
        if (!mFragments.containsKey(itemId)) {
            // TODO(133419201): check if a Fragment provided here is a new Fragment
            Fragment newFragment = createFragment(position);
            newFragment.setInitialSavedState(mSavedStates.get(itemId));
            mFragments.put(itemId, newFragment);
        }
    }

mFragments成员变量缓存了所以使用过的Fragment,gcFragments() 方法在几处被调用,在绑定ViewHolder时是触发垃圾回收的时机之一:

void gcFragments() {
        if (!mHasStaleFragments || shouldDelayFragmentTransactions()) {
            return;
        }

        // Remove Fragments for items that are no longer part of the data-set
        //对于position不在[0, getItemCount()]内的Fragment应该删除
        Set<Long> toRemove = new ArraySet<>();
        for (int ix = 0; ix < mFragments.size(); ix++) {
            long itemId = mFragments.keyAt(ix);
            if (!containsItem(itemId)) {
                toRemove.add(itemId);
                mItemIdToViewHolder.remove(itemId); // in case they're still bound
            }
        }

        // Remove Fragments that are not bound anywhere -- pending a grace period
        //对于没有被绑定到ViewHolder中的Fragment应该删除
        if (!mIsInGracePeriod) {
            mHasStaleFragments = false; // we've executed all GC checks

            for (int ix = 0; ix < mFragments.size(); ix++) {
                long itemId = mFragments.keyAt(ix);
                if (!isFragmentViewBound(itemId)) {
                    toRemove.add(itemId);
                }
            }
        }

        for (Long itemId : toRemove) {
            removeFragment(itemId);
        }
    }

c. 重写onViewAttachedToWindow()方法

重写该方法做两件事:

  1. 将Fragement添加到ViewHolder中
  2. 垃圾回收无用Fragment
public final void onViewAttachedToWindow(@NonNull final FragmentViewHolder holder) {
        placeFragmentInViewHolder(holder);
        gcFragments();
    }

d. 重写onAttachedToRecyclerView()方法

public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        checkArgument(mFragmentMaxLifecycleEnforcer == null);
        mFragmentMaxLifecycleEnforcer = new FragmentMaxLifecycleEnforcer();
        mFragmentMaxLifecycleEnforcer.register(recyclerView);
    }

主要是构造一个FragmentMaxLifecycleEnforcer对象,并将recyclerView注册给该对象,管理当前主Fragment与其余非主Fragment的生命周期,主要通过其updateFragmentMaxLifecycle()方法来实现:

void updateFragmentMaxLifecycle(boolean dataSetChanged) {
			//删除部分状态判断代码
			//...
            long currentItemId = getItemId(currentItem);
            if (currentItemId == mPrimaryItemId && !dataSetChanged) {
                return; // nothing to do
            }

            Fragment currentItemFragment = mFragments.get(currentItemId);
            if (currentItemFragment == null || !currentItemFragment.isAdded()) {
                return;
            }

            mPrimaryItemId = currentItemId;
            FragmentTransaction transaction = mFragmentManager.beginTransaction();
			//寻找即将resume的Fragment
            Fragment toResume = null;
            for (int ix = 0; ix < mFragments.size(); ix++) {
                long itemId = mFragments.keyAt(ix);
                Fragment fragment = mFragments.valueAt(ix);

                if (!fragment.isAdded()) {
                    continue;
                }
				//对于不是寻找的对象,即将其最大生命周期设置为STARTED
                if (itemId != mPrimaryItemId) {
                    transaction.setMaxLifecycle(fragment, STARTED);
                } else {
                    toResume = fragment; // itemId map key, so only one can match the predicate
                }

                fragment.setMenuVisibility(itemId == mPrimaryItemId);
            }
            //将寻找到的Fragment的最大生命周期设置为RESUMED
            if (toResume != null) { // in case the Fragment wasn't added yet
                transaction.setMaxLifecycle(toResume, RESUMED);
            }

            if (!transaction.isEmpty()) {
                transaction.commitNow();
            }
        }

e. 重写onViewRecycled()方法

RecyclerView在回收ViewHolder并将其添加到Pools中时会调用onViewRecycled()方法,FragmentStateAdapter重写该方法将缓存对象从mFragments和mItemIdToViewHolder中删除:

public final void onViewRecycled(@NonNull FragmentViewHolder holder) {
        final int viewHolderId = holder.getContainer().getId();
        final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
        if (boundItemId != null) {
            removeFragment(boundItemId);
            mItemIdToViewHolder.remove(boundItemId);
        }
    }

f. 实现StatefulAdapter接口的saveState()方法和restoreState()方法

和FragmentStatePagerAdapter一样,StatefulAdapter也有两个成员变量mFagment和mSavedStates,实现这两个方法主要是为了存储和恢复mFagment/mSavedStates。

Notice

  1. FragmentStateAdapter的内部维护了一个fragmens数组,对于单个Fragment的创建其暴露给使用者来实现,然而Fragment的销毁也是其在内部维护的,外部不知道何时销毁Fragment。这就没有FragmentStatePagerAdapter使用起来方便了,其不仅暴露了创建Fragment的方法instantiateItem(),而且还暴露了Fragment销毁的方法destroyItem(),因此开发人员可以自己在其子类维护一个fragments数组。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值