ViewPager2的使用
ViewPager2實(shí)際是內(nèi)部的RecyclerView在起作用,所以ViewPager2一定要設(shè)置RecyclerView.Adapter賦值,并且Adapter的ItemView的布局必須為match_parent。才能正常使用。ViewPager2+Fragment提供了一個新的適配器FragmentStateAdapter。
ViewPager2+Fragment代碼實(shí)例
FragmentStateAdapter實(shí)例
class ViewPagerAdapter : FragmentStateAdapter {
private var fragmentList: MutableList<Fragment> = ArrayList()
constructor(fragmentActivity: FragmentActivity) : super(fragmentActivity)
constructor(fragment: Fragment) : super(fragment)
constructor(fragmentManager: FragmentManager, lifecycle: Lifecycle) : super(
fragmentManager,
lifecycle
)
fun addFragment(fragment: Fragment?) {
if (fragment == null) {
return
}
fragmentList.add(fragment)
}
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
Activity中為Viewpager2初始化設(shè)置Adapter
val pageAdapter = ViewPagerAdapter(this)
pageAdapter.addFragment(FirstFragment())
pageAdapter.addFragment(TwoFragment())
pageAdapter.addFragment(ThreeFragment())
viewPage2.adapter = pageAdapter
ViewPager2的常見的API:
// 可滑動的方向
viewPage?.orientation = ViewPager2.ORIENTATION_HORIZONTAL
// 用戶是否可滑動
viewPage?.isUserInputEnabled = true
// 頁面滑動監(jiān)聽
viewPage?.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { //監(jiān)聽事件
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
}
})
// 設(shè)置轉(zhuǎn)場動畫
val compositePageTransformer = CompositePageTransformer()
compositePageTransformer.addTransformer(MarginPageTransformer(40))
compositePageTransformer.addTransformer(ScaleTransformer())
viewPage?.setPageTransformer(compositePageTransformer)
viewPage?.beginFakeDrag() // 開始模擬滑動
if (viewPage?.fakeDragBy(-500f) == true) { //模擬滑動
viewPage?.endFakeDrag() //結(jié)束模擬滑動
}
以上就是ViewPager2的使用和屬性設(shè)置,功能還是比較強(qiáng)大的,接下來看看Viewpager2的源碼進(jìn)行原理分析。
ViewPager2原理淺析
從ViewPager2的構(gòu)造函數(shù)開始分析:
public ViewPager2(@NonNull Context context) {
super(context);
initialize(context, null);
}
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public ViewPager2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
//...
// 初始化RecyclerView
mRecyclerView = new RecyclerViewImpl(context);
mRecyclerView.setId(ViewCompat.generateViewId());
mRecyclerView.setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
// 初始化LayoutManager,所以我們不用設(shè)置LayoutManager
mLayoutManager = new LinearLayoutManagerImpl(context);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
setOrientation(context, attrs);
mRecyclerView.setLayoutParams(
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mRecyclerView.addOnChildAttachStateChangeListener(enforceChildFillListener());
//創(chuàng)建滑動事件轉(zhuǎn)換器對象,通過監(jiān)聽RecyclerView的監(jiān)聽把狀態(tài)傳給ViewPager2
mScrollEventAdapter = new ScrollEventAdapter(this);
// 創(chuàng)建模擬拖動器對象,用于提供代碼模擬滑動
mFakeDragger = new FakeDrag(this, mScrollEventAdapter, mRecyclerView);
// 創(chuàng)建PagerSnapHelper對象,實(shí)現(xiàn)頁面切換居中且只滑動一頁的效果
mPagerSnapHelper = new PagerSnapHelperImpl();
mPagerSnapHelper.attachToRecyclerView(mRecyclerView);
// 設(shè)置RecyclerView的滑動監(jiān)聽
mRecyclerView.addOnScrollListener(mScrollEventAdapter);
//....
}
// 重新封裝了RecyclerView,處理攔截事件
private class RecyclerViewImpl extends RecyclerView {
// 設(shè)置用戶是否可以滑動控件
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
return isUserInputEnabled() && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isUserInputEnabled() && super.onInterceptTouchEvent(ev);
}
}
// 封裝LinearLayoutManager,處理緩存頁面
private class LinearLayoutManagerImpl extends LinearLayoutManager {
// 設(shè)置緩存頁面
@Override
protected void calculateExtraLayoutSpace(@NonNull RecyclerView.State state,
@NonNull int[] extraLayoutSpace) {
int pageLimit = getOffscreenPageLimit();
if (pageLimit == OFFSCREEN_PAGE_LIMIT_DEFAULT) {
// Only do custom prefetching of offscreen pages if requested
super.calculateExtraLayoutSpace(state, extraLayoutSpace);
return;
}
final int offscreenSpace = getPageSize() * pageLimit;
extraLayoutSpace[0] = offscreenSpace;
extraLayoutSpace[1] = offscreenSpace;
}
}
// 封裝PagerSnapHelper,處理模擬拖動時的目標(biāo)View
private class PagerSnapHelperImpl extends PagerSnapHelper {
// 如果正在模擬拖動則不設(shè)置目標(biāo)View
@Nullable
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
return isFakeDragging() ? null : super.findSnapView(layoutManager);
}
}
在ViewPager2的構(gòu)造方法中都會調(diào)用initialize方法進(jìn)行初始化,其中實(shí)例化了一個封裝的RecyclerView,并為這個RecyclerView設(shè)置了layoutManager、OnScrollListener監(jiān)聽、SnapHelper。
- 封裝了RecyclerView,主要是
onTouchEvent、onInterceptTouchEvent多加了isUserInputEnabled變量來判斷是否攔截事件,從而實(shí)現(xiàn)控制用戶是否可以滑動功能。 - 封裝了LinearLayoutManager,主要是在
calculateExtraLayoutSpace方法中根據(jù)OffscreenPageLimit擴(kuò)展可用的空間,從而實(shí)現(xiàn)頁面的緩存功能。 - 封裝了PagerSnapHelper,主要是在
findSnapView中判斷是否正在模擬滑動中,如果是則不設(shè)置目標(biāo)View。它的作用是使RecyclerView每次只能滑動一頁,而且ItemView會自動居中顯示。PagerSnapHelper在這篇不在分析,不熟悉可以看看之前寫的文章:RecycerView擴(kuò)展SnapHepler源碼分析 -
mScrollEventAdapter是RecyclerView滑動監(jiān)聽的對象,把RecyclerView的滑動監(jiān)聽狀態(tài)轉(zhuǎn)換成ViewPager2的OnPageChangeCallback的三種狀態(tài)。 -
mFakeDragger是實(shí)現(xiàn)模擬拖動器的對象。
ScrollEventAdapter
ScrollEventAdapter直譯意思是:滑動事件適配器。類如其名,它的作用就是將RecyclerView的滑動事件適配成ViewPager2的OnPageChangeCallback的事件
public abstract static class OnPageChangeCallback {
// 頁面正在滑動時調(diào)用
public void onPageScrolled(int position, float positionOffset,
@Px int positionOffsetPixels) {
}
// 頁面切換后調(diào)用
public void onPageSelected(int position) {
}
// 頁面狀態(tài)發(fā)生變化后調(diào)用:開始滑動前、手勢離開后、滑動停止
public void onPageScrollStateChanged(@ScrollState int state) {
}
}
final class ScrollEventAdapter extends RecyclerView.OnScrollListener {
private OnPageChangeCallback mCallback;
ScrollEventAdapter(@NonNull ViewPager2 viewPager) {
// 根據(jù)ViewPager2拿到一些相關(guān)信息
mViewPager = viewPager;
mRecyclerView = mViewPager.mRecyclerView;
//noinspection ConstantConditions
mLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
mScrollValues = new ScrollEventValues();
resetState(); // 變量初始化
}
// 給ViewPager2設(shè)置
void setOnPageChangeCallback(OnPageChangeCallback callback) {
mCallback = callback;
}
// 將RecyclerView的滑動適配成 頁面滑動狀態(tài)變化
private void dispatchStateChanged(@ScrollState int state) {
if (mAdapterState == STATE_IN_PROGRESS_IMMEDIATE_SCROLL
&& mScrollState == SCROLL_STATE_IDLE) {
return;
}
if (mScrollState == state) {
return;
}
mScrollState = state;
if (mCallback != null) {
mCallback.onPageScrollStateChanged(state);
}
}
// 將RecyclerView的滑動適配成 頁面切換完成狀態(tài)
private void dispatchSelected(int target) {
if (mCallback != null) {
mCallback.onPageSelected(target);
}
}
// 將RecyclerView的滑動適配成 頁面滑動
private void dispatchScrolled(int position, float offset, int offsetPx) {
if (mCallback != null) {
mCallback.onPageScrolled(position, offset, offsetPx);
}
}
// 這個方法只有在RecyclerView滑動之前、手勢離開屏幕后、滑動結(jié)束后會調(diào)用。
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
// 滑動之前
if ((mAdapterState != STATE_IN_PROGRESS_MANUAL_DRAG
|| mScrollState != SCROLL_STATE_DRAGGING)
&& newState == RecyclerView.SCROLL_STATE_DRAGGING) {
// 此方法隨后調(diào)用 dispatchStateChanged(SCROLL_STATE_DRAGGING)適配成Pager的事件
startDrag(false);
return;
}
// 手指離開后
if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_SETTLING) {
if (mScrollHappened) {
// 調(diào)用dispatchStateChanged(SCROLL_STATE_SETTLING) 適配成Page事件
dispatchStateChanged(SCROLL_STATE_SETTLING);
mDispatchSelected = true;
}
return;
}
// 滑動結(jié)束后
if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_IDLE) {
boolean dispatchIdle = false;
updateScrollEventValues();
if (!mScrollHappened) {
// 在拖動過程中頁面沒有移動,因此我們位于列表的開頭或結(jié)尾。或根本沒有頁面。
// 在第一種情況下,ViewPager的合同要求至少一個滾動事件。在第二種情況下,不要發(fā)送該滾動事件
if (mScrollValues.mPosition != RecyclerView.NO_POSITION) {
// 如果在頭尾則給一個滑動事件
dispatchScrolled(mScrollValues.mPosition, 0f, 0);
}
dispatchIdle = true;
} else if (mScrollValues.mOffsetPx == 0) {
dispatchIdle = true;
if (mDragStartPosition != mScrollValues.mPosition) {
// 滑動完成切換事件
dispatchSelected(mScrollValues.mPosition);
}
}
if (dispatchIdle) {
// 滑動狀態(tài)改變事件
dispatchStateChanged(SCROLL_STATE_IDLE);
resetState();
}
}
if (mAdapterState == STATE_IN_PROGRESS_SMOOTH_SCROLL
&& newState == RecyclerView.SCROLL_STATE_IDLE && mDataSetChangeHappened) {
updateScrollEventValues();
if (mScrollValues.mOffsetPx == 0) {
if (mTarget != mScrollValues.mPosition) {
// 滑動切換完成事件
dispatchSelected(
mScrollValues.mPosition == NO_POSITION ? 0 : mScrollValues.mPosition);
}
// 滑動狀態(tài)發(fā)生變化事件
dispatchStateChanged(SCROLL_STATE_IDLE);
resetState();
}
}
}
// recyclerView滑動時會調(diào)用
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
mScrollHappened = true;
updateScrollEventValues();
// 在手指滑動時離開屏幕時mDispatchSelected設(shè)置為true
if (mDispatchSelected) {
// 檢車是否滑動完成,調(diào)用dispatchSelected()方法適配調(diào)用
// Drag started settling, need to calculate target page and dispatch onPageSelected now
mDispatchSelected = false;
boolean scrollingForward = dy > 0 || (dy == 0 && dx < 0 == mViewPager.isRtl());
// "&& values.mOffsetPx != 0": filters special case where we're scrolling forward and
// the first scroll event after settling already got us at the target
mTarget = scrollingForward && mScrollValues.mOffsetPx != 0
? mScrollValues.mPosition + 1 : mScrollValues.mPosition;
if (mDragStartPosition != mTarget) {
dispatchSelected(mTarget);
}
} else if (mAdapterState == STATE_IDLE) {
// onScrolled while IDLE means RV has just been populated after an adapter has been set.
// Contract requires us to fire onPageSelected as well.
int position = mScrollValues.mPosition;
// Contract forbids us to send position = -1 though
dispatchSelected(position == NO_POSITION ? 0 : position);
}
// 調(diào)用dispatchScrolled適配頁面滑動。如果位置是-1,則用0代替。
dispatchScrolled(mScrollValues.mPosition == NO_POSITION ? 0 : mScrollValues.mPosition,
mScrollValues.mOffset, mScrollValues.mOffsetPx);
// Dispatch idle in onScrolled instead of in onScrollStateChanged because RecyclerView
// doesn't send IDLE event when using setCurrentItem(x, false)
if ((mScrollValues.mPosition == mTarget || mTarget == NO_POSITION)
&& mScrollValues.mOffsetPx == 0 && !(mScrollState == SCROLL_STATE_DRAGGING)) {
// When the target page is reached and the user is not dragging anymore, we're settled,
// so go to idle.
// Special case and a bit of a hack when mTarget == NO_POSITION: RecyclerView is being
// initialized and fires a single scroll event. This flags mScrollHappened, so we need
// to reset our state. However, we don't want to dispatch idle. But that won't happen;
// because we were already idle.
dispatchStateChanged(SCROLL_STATE_IDLE);
resetState();
}
}
}
ScrollEventAdapter作為一個RecyclerView.OnScrollListener的實(shí)例類。在監(jiān)聽RecyclerView的滑動狀態(tài)的兩個方法onScrollStateChanged、onScrolled中,根據(jù)不同的狀態(tài)調(diào)用dispatchStateChanged、dispatchSelected、dispatchScrolled設(shè)配成ViewPager2的頁面滑動接口OnPageChangeCallback。
PageTransformerAdapter
PageTransformerAdapter是ViewPager2.OnPageChangeCallback的實(shí)例類,作用是把OnPageChangeCallback.onPageScrolled的事件適配成PageTransformer.transformPage()事件。
先看看PageTransformerAdapter在ViewPager2被調(diào)用的地方:
private void initialize(Context context, AttributeSet attrs) {
// ....
// 初始化PageTransformerAdapter,并把實(shí)例添加進(jìn)CompositeOnPageChangeCallback中
mPageTransformerAdapter = new PageTransformerAdapter(mLayoutManager);
mPageChangeEventDispatcher.addOnPageChangeCallback(mPageTransformerAdapter);
}
public void setPageTransformer(@Nullable PageTransformer transformer) {
if (transformer != null) {
if (!mSavedItemAnimatorPresent) {
mSavedItemAnimator = mRecyclerView.getItemAnimator();
mSavedItemAnimatorPresent = true;
}
mRecyclerView.setItemAnimator(null);
} else {
if (mSavedItemAnimatorPresent) {
mRecyclerView.setItemAnimator(mSavedItemAnimator);
mSavedItemAnimator = null;
mSavedItemAnimatorPresent = false;
}
}
// TODO: add support for reverseDrawingOrder: b/112892792
// TODO: add support for pageLayerType: b/112893074
if (transformer == mPageTransformerAdapter.getPageTransformer()) {
return;
}
// 將設(shè)置進(jìn)來的PageTransformer給mPageTransformerAdapter
mPageTransformerAdapter.setPageTransformer(transformer);
requestTransform();
}
public interface PageTransformer {
/**
* Apply a property transformation to the given page.
*
* @param page Apply the transformation to this page
* @param position Position of page relative to the current front-and-center
* position of the pager. 0 is front and center. 1 is one full
* page position to the right, and -2 is two pages to the left.
* Minimum / maximum observed values depend on how many pages we keep
* attached, which depends on offscreenPageLimit.
*
* @see #setOffscreenPageLimit(int)
*/
void transformPage(@NonNull View page, float position);
}
final class PageTransformerAdapter extends OnPageChangeCallback {
private final LinearLayoutManager mLayoutManager;
private PageTransformer mPageTransformer;
PageTransformerAdapter(LinearLayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
PageTransformer getPageTransformer() {
return mPageTransformer;
}
/**
* Sets the PageTransformer. The page transformer will be called for each attached page whenever
* the scroll position is changed.
*
* @param transformer The PageTransformer
*/
void setPageTransformer(@Nullable PageTransformer transformer) {
// TODO: add support for reverseDrawingOrder: b/112892792
// TODO: add support for pageLayerType: b/112893074
mPageTransformer = transformer;
}
// 在onPageScrolled的事件轉(zhuǎn)換成PageTransformer.transformPage事件
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mPageTransformer == null) {
return;
}
float transformOffset = -positionOffset;
// 能遍歷多少,取決于緩存了多少個ItemView
for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
View view = mLayoutManager.getChildAt(i);
if (view == null) {
throw new IllegalStateException(String.format(Locale.US,
"LayoutManager returned a null child at pos %d/%d while transforming pages",
i, mLayoutManager.getChildCount()));
}
//以ViewPager的中心為原點(diǎn),左為負(fù),右為正,當(dāng)前View的中心點(diǎn)距離ViewPager的中心的距離。
int currPos = mLayoutManager.getPosition(view);
float viewOffset = transformOffset + (currPos - position);
mPageTransformer.transformPage(view, viewOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
PageTransformerAdapter的作用就是為ViewPager2的PageTransformer,在頁面滑動的時候轉(zhuǎn)換成PageTransformer. transformPage()。其中的transformPage(@NonNull View page, float position)方法的參數(shù)值可以參考這篇文章PageTransformer詳解。ViewPager2如果想要實(shí)現(xiàn)頁面之間的轉(zhuǎn)換時動畫可以重寫PageTransformer
FragmentStateAdapter
FragmentStateAdapter是谷歌為我們實(shí)現(xiàn)適配加載Fragment的RecyclerView.Adapter,如果我們想用ViewPager2加載Fragment頁面,就可以繼承FragmentStateAdapter作為適配器,只要重寫其構(gòu)造方法和createFragment、getItemCount。
class VPFragmentAdapter2 : FragmentStateAdapter {
private val fragmentList: MutableList<Fragment> = ArrayList()
constructor(fragmentActivity: FragmentActivity) : super(fragmentActivity)
constructor(fragment: Fragment) : super(fragment)
constructor(fragmentManager: FragmentManager, lifecycle: Lifecycle) : super(
fragmentManager,
lifecycle
)
fun addFragment(fragment: Fragment) {
fragmentList.add(fragment)
}
fun getCurrentFragment(index: Int): Fragment? {
if (index >= 0 && index < fragmentList.size) {
return fragmentList[index]
}
return null
}
fun clear() {
fragmentList.clear()
}
//
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
// RecyclerView.Adapter的抽象方法
override fun getItemCount(): Int {
return fragmentList.size
}
}
這就是一個簡易版的ViewPager2的FragmentAdapter。由此可見FragmentStateAdapter幫我們實(shí)現(xiàn)了Adapter的onCreateViewHolder、onBindViewHolder抽象方法。在onCreateViewHolder方法中創(chuàng)建了一個以FrameLayout為父類的View裝載Fragment。
public abstract class FragmentStateAdapter extends
RecyclerView.Adapter<FragmentViewHolder> implements StatefulAdapter {
@SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
//HashMap (position,Fragment)
final LongSparseArray<Fragment> mFragments = new LongSparseArray<>();
//HashMap (position,Fragment.SavedState)
private final LongSparseArray<Fragment.SavedState> mSavedStates = new LongSparseArray<>();
//HashMap (position,ViewId)
private final LongSparseArray<Integer> mItemIdToViewHolder = new LongSparseArray<>();
public abstract @NonNull Fragment createFragment(int position);
@NonNull
@Override
public final FragmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 創(chuàng)建一個以FrameLayout的父容器
return FragmentViewHolder.create(parent);
}
@Override
public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
final long itemId = holder.getItemId();
// FrameLayout的Id
final int viewHolderId = holder.getContainer().getId();
// 通過HashMap,根據(jù)ViewId得到此之前加載的position
final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
// 此ViewHolder之前加載過Fragment,且與需要加載Fragment不同,則將ViewHolder的舊Fragment移除
if (boundItemId != null && boundItemId != itemId) {
// 移除FrameLayout的Fragment和HashMap的數(shù)據(jù)
removeFragment(boundItemId);
// 移除HashMap的數(shù)據(jù)
mItemIdToViewHolder.remove(boundItemId);
}
// 重新設(shè)置HashMap
mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
ensureFragment(position);
// 特殊情況,當(dāng)RecyclerView讓ItemView保持在Window,但是不在視圖樹中。
final FrameLayout container = holder.getContainer();
if (ViewCompat.isAttachedToWindow(container)) {
if (container.getParent() != null) {
throw new IllegalStateException("Design assumption violated.");
}
// 當(dāng)ItemView添加在到RecyclerView中才加載Fragment
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);
// 加載Fragment
placeFragmentInViewHolder(holder);
}
}
});
}
gcFragments();
}
// 移除FrameLayout的Fragment和HashMap的數(shù)據(jù)
private void removeFragment(long itemId) {
// 得到相應(yīng)位置的Fragment
Fragment fragment = mFragments.get(itemId);
// fragment為空,則不往下執(zhí)行
if (fragment == null) {
return;
}
// 將FrameLayout清空子View布局
if (fragment.getView() != null) {
ViewParent viewParent = fragment.getView().getParent();
if (viewParent != null) {
((FrameLayout) viewParent).removeAllViews();
}
}
// 移除舊位置的狀態(tài)
if (!containsItem(itemId)) {
mSavedStates.remove(itemId);
}
// fragment已經(jīng)沒有依附,則移除mFragments對應(yīng)的位置,并return
if (!fragment.isAdded()) {
mFragments.remove(itemId);
return;
}
// 應(yīng)該延遲Fragment交易
if (shouldDelayFragmentTransactions()) {
mHasStaleFragments = true;
return;
}
if (fragment.isAdded() && containsItem(itemId)) {
mSavedStates.put(itemId, mFragmentManager.saveFragmentInstanceState(fragment));
}
mFragmentManager.beginTransaction().remove(fragment).commitNow();
mFragments.remove(itemId);
}
// 更新mFragments的數(shù)據(jù)
private void ensureFragment(int position) {
long itemId = getItemId(position);
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);
}
}
}
在重寫的onCreateViewHolder方法中,通過FragmentViewHolder.create創(chuàng)建一個以FrameLayout為父容器的ItemView。
在重寫的onBindViewHolder方法中,1. 先檢測ItemView是否有加載過Fragment,如果加載過的Fragment與現(xiàn)在的Fragment不相同,則對ItemView和三個HashMap進(jìn)行數(shù)據(jù)清空。2. 對兩個HashMap設(shè)置新的數(shù)據(jù)。 3. 如果存在特殊情況,則當(dāng)ItemView添加在到RecyclerView中才加載Fragment。
在ItemView依附在RecyclerView時,開始加載Fragment
具體能有多少個ItemView依附在RecyclerView中,取決于ViewPager2的緩存大小。
@Override
public final void onViewAttachedToWindow(@NonNull final FragmentViewHolder holder) {
// 加載Fragment
placeFragmentInViewHolder(holder);
gcFragments();
}
// 加載Fragment
@SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) {
// 通過mFragments拿到Fragment和Fragment的View
Fragment fragment = mFragments.get(holder.getItemId());
if (fragment == null) {
throw new IllegalStateException("Design assumption violated.");
}
FrameLayout container = holder.getContainer();
View view = fragment.getView();
/ *可能的狀態(tài):
-fragment:{添加,未添加}
-視圖:{已創(chuàng)建,未創(chuàng)建}
-視圖:{已附加,未附加}
組合:
-{f:已添加,v:已創(chuàng)建,v:已附加}->檢查是否已附加到正確的容器
-{f:已添加,v:已創(chuàng)建,v:沒附加}->將視圖附加到容器
-{f:已添加,v:沒創(chuàng)建,v:已附加}->不可能
-{f:已添加,v:沒創(chuàng)建 ,v:沒附加}->計劃創(chuàng)建時的調(diào)用
-{f:沒添加,v:已創(chuàng)建,v:已附加}->非法狀態(tài)
-{f:沒添加,v:已創(chuàng)建,v:沒附加}->非法狀態(tài)
-{f:沒添加,v:沒創(chuàng)建,v:已附加}->不可能
-{f:沒添加,v:沒創(chuàng)建,v:沒附加}->添加,創(chuàng)建,附加
* /
// { f:沒添加, v:已創(chuàng)建, v:attached } -> 非法狀態(tài)
// { f:沒添加, v:已創(chuàng)建, v:notAttached } ->非法狀態(tài)
if (!fragment.isAdded() && view != null) {
throw new IllegalStateException("Design assumption violated.");
}
// {f:添加,v:沒創(chuàng)建,v:沒附加}->計劃創(chuàng)建時的回調(diào)
if (fragment.isAdded() && view == null) {
scheduleViewAttach(fragment, container);
return;
}
//{f:添加,v:創(chuàng)建,v:附加}->檢查是否附加到正確的容器
if (fragment.isAdded() && view.getParent() != null) {
if (view.getParent() != container) {
addViewToContainer(view, container);
}
return;
}
//{f:已添加,v:已創(chuàng)建,v:未附加}->將視圖附加到容器
if (fragment.isAdded()) {
addViewToContainer(view, container);
return;
}
//{f:沒添加,v:沒創(chuàng)建,v:未附加}->添加,創(chuàng)建,附加
if (!shouldDelayFragmentTransactions()) {
scheduleViewAttach(fragment, container);
mFragmentManager.beginTransaction()
.add(fragment, "f" + holder.getItemId())
.setMaxLifecycle(fragment, STARTED)
.commitNow();
mFragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(false);
} else {
if (mFragmentManager.isDestroyed()) {
return; // nothing we can do
}
mLifecycle.addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (shouldDelayFragmentTransactions()) {
return;
}
source.getLifecycle().removeObserver(this);
if (ViewCompat.isAttachedToWindow(holder.getContainer())) {
placeFragmentInViewHolder(holder);
}
}
});
}
}
// 檢查是否附加到正確的容器,將視圖附加到容器
@SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
void addViewToContainer(@NonNull View v, @NonNull FrameLayout container) {
if (container.getChildCount() > 1) {
throw new IllegalStateException("Design assumption violated.");
}
if (v.getParent() == container) {
return;
}
if (container.getChildCount() > 0) {
container.removeAllViews();
}
if (v.getParent() != null) {
((ViewGroup) v.getParent()).removeView(v);
}
container.addView(v);
}
// 計劃創(chuàng)建時的回調(diào)
private void scheduleViewAttach(final Fragment fragment, @NonNull final FrameLayout container) {
// After a config change, Fragments that were in FragmentManager will be recreated. Since
// ViewHolder container ids are dynamically generated, we opted to manually handle
// attaching Fragment views to containers. For consistency, we use the same mechanism for
// all Fragment views.
mFragmentManager.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
// TODO(b/141956012): Suppressed during upgrade to AGP 3.6.
@SuppressWarnings("ReferenceEquality")
@Override
public void onFragmentViewCreated(@NonNull FragmentManager fm,
@NonNull Fragment f, @NonNull View v,
@Nullable Bundle savedInstanceState) {
if (f == fragment) {
fm.unregisterFragmentLifecycleCallbacks(this);
addViewToContainer(v, container);
}
}
}, false);
}
加載Fragment的流程在就在placeFragmentInViewHolder方法里,通過檢查fragment和View的狀態(tài)分別作出不同的操作。
關(guān)于ViewPager2的源碼淺析就講完了,下面總結(jié)一下:
總結(jié)
ScrollEventAdapter的作用:是將RecyclerView的滑動事件轉(zhuǎn)換成ViewPager2的OnPageChangeCallBack。
PageTransformerAdapter的作用:是將RecyclerView的滑動事件轉(zhuǎn)換成ViewPager2的PageTransformer。
FragmentStateAdapter的作用:是為ViewPager2加載Fragment提供基礎(chǔ)的Adapter。