android viewpager 缓存,Android-ViewPager源码解析与性能优化

一、高度设置wrap_content或者具体的值无效

ViewPager高度设置为wrap_content或者具体的高度值无效,是因为ViewPager的onMeasure方法在度量宽高的时候,在方法体的最开始就直接调用了setMeasuredDimension()方法将自身的宽高度量,但是并没有在其onMeasure()计算完其具体的子View的宽高之后,重新度量一次自身的宽高

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// For simple implementation, our internal size is always 0.

// We depend on the container to specify the layout size of

// our view. We can't really know what it is since we will be

// adding and removing different arbitrary views and do not

// want the layout to change as this happens.

setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),

getDefaultSize(0, heightMeasureSpec));

...

}

public static int getDefaultSize(int size, int measureSpec) {

int result = size;

int specMode = MeasureSpec.getMode(measureSpec);

int specSize = MeasureSpec.getSize(measureSpec);

switch (specMode) {

case MeasureSpec.UNSPECIFIED:

result = size;

break;

case MeasureSpec.AT_MOST:

case MeasureSpec.EXACTLY:

result = specSize;

break;

}

return result;

}

从这里我们可以看到,ViewPager的宽高会受其父容器的宽高的限制,但是并不会因为自身子View的宽高而影响ViewPager的宽高。

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {

boolean optical = isLayoutModeOptical(this);

if (optical != isLayoutModeOptical(mParent)) {

Insets insets = getOpticalInsets();

int opticalWidth = insets.left + insets.right;

int opticalHeight = insets.top + insets.bottom;

measuredWidth += optical ? opticalWidth : -opticalWidth;

measuredHeight += optical ? opticalHeight : -opticalHeight;

}

setMeasuredDimensionRaw(measuredWidth, measuredHeight);

}

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {

// ViewPager的度量宽高

mMeasuredWidth = measuredWidth;

mMeasuredHeight = measuredHeight;

mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;

}

看setMeasuredDimension的源码调用可以看出,当父容器的高度确定时,ViewPager的宽高其实就是父容器的宽高,ViewPager就是在onMeasure方法一进来的时候就直接填充满整个父容器的剩余空间。在计算孩子节点之前,就已经计算好了ViewPager的宽高,在计算完孩子节点之后,并不会再去重新计算ViewPager的宽高。

二、解决ViewPager的wrap_content或者100dp这样具体高度无效的问题

自定义一个ViewPager,根据子View的宽高重新度量ViewPager的宽高。其实做法就是在自定义onMeasure的super.onMeasure(widthMeasureSpec, heightMeasureSpec);之前重新计算heightMeasureSpec,将原本ViewPager接收的父容器的限定的heightMeasureSpec替换成我们自定义的heightMeasureSpec。

public class NoScrollWrapContentViewPager extends ViewPager {

private boolean noScroll = false;

private int current;

private int height = 0;

/**

* 保存position与对于的View

*/

private HashMap mChildrenViews = new LinkedHashMap();

public NoScrollWrapContentViewPager(Context context, AttributeSet attrs) {

super(context, attrs);

}

public NoScrollWrapContentViewPager(Context context) {

super(context);

}

public void setNoScroll(boolean noScroll) {

this.noScroll = noScroll;

}

@Override

public void scrollTo(int x, int y) {

super.scrollTo(x, y);

}

@Override

public boolean onTouchEvent(MotionEvent arg0) {

if (noScroll)

return false;

else

return super.onTouchEvent(arg0);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent arg0) {

if (noScroll)

return false;

else

return super.onInterceptTouchEvent(arg0);

}

@Override

public void setCurrentItem(int item, boolean smoothScroll) {

super.setCurrentItem(item, smoothScroll);

}

@Override

public void setCurrentItem(int item) {

super.setCurrentItem(item);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// int height = 0;

// for (int i = 0; i < getChildCount(); i++) {

// View child = getChildAt(i);

// child.measure(widthMeasureSpec,

// MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

// int h = child.getMeasuredHeight();

// if (h > height)

// height = h;

// }

// heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,

// MeasureSpec.EXACTLY);

// super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (mChildrenViews.size() > current) {

View child = mChildrenViews.get(current);

child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

height = child.getMeasuredHeight();

}

heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

public void resetHeight(int current) {

this.current = current;

if (mChildrenViews.size() > current) {

LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();

if (layoutParams == null) {

layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height);

} else {

layoutParams.height = height;

}

setLayoutParams(layoutParams);

}

}

/**

* 保存position与对于的View

*/

public void setObjectForPosition(View view, int position) {

mChildrenViews.put(position, view);

}

}

但是这样的做法,会有种问题,即在ViewPager的子View是采用LinearLayout作为根布局的时候,并且给LinearLayout设置了固定的高度值,那么会出现ViewPager动态高度无效的问题

其实具体的做法,就是仿造measureChild的做法,自定义子View的heightMeasureSpec然后度量整个子View,其实子View的宽度也可以这样做。

public class MyViewPager extends ViewPager {

private static final String TAG = "MyViewPager";

public MyViewPager(@NonNull Context context) {

super(context);

}

public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int height = 0;

int count = getChildCount();

for (int i=0;i

View child = getChildAt(i);

// 自定义子View的MeasureSpec,可以参考measureChild的做法,需要通过子View的LayoutParams

ViewGroup.LayoutParams lp = child.getLayoutParams();

child.measure(widthMeasureSpec, getChildMeasureSpec(heightMeasureSpec, 0, lp.height));

int h = child.getMeasuredHeight();

Log.e(TAG, "测量的子View的宽高:" + h);

if (h > height) {

height = h;

}

}

heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

}

三、ViewPager缓存机制解析

1.setOffscreenPageLimit为什么缓存的页面数不能低于1?

这里其实是源码层做了限制,在setOffscreenPageLimit中设置了一个默认值,而这个默认值的大小为1

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

public void setOffscreenPageLimit(int limit) {

if (limit < DEFAULT_OFFSCREEN_PAGES) {

Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +

DEFAULT_OFFSCREEN_PAGES);

limit = DEFAULT_OFFSCREEN_PAGES;

}

if (limit != mOffscreenPageLimit) {

mOffscreenPageLimit = limit;

populate();

}

}

所以从这里可以看出,ViewPager的最小缓存的limit是1,而不能小于1,当小于1的时候就会被强制的设置为1。

而populate()函数就是用来处理ViewPager的缓存的。

populate()的生命周期是与Adapter的生命周期绑定的。

其实在setOffscreenPageLimit()的时候,调用的populate(),而populate()内部调用的

public void populate() {

populate(mCurItem);

}

而pupulate(int newCurrentItem)方法在另一处调用的地方就是在setCurrentItem。

其实ViewPager缓存都是基于ItemInfo这个类来进行的,

void populate(int newCurrentItem) {

ItemInfo oldCurInfo = null;

int focusDirection = View.FOCUS_FORWARD;

// 重置mCurItem,当ViewPager从当前页面滑动到下一个页面时

// 这个时候mCurItem就会发生改变,populate的参数是新页面的position

// 所以需要修改mCurItem为新的position

if (mCurItem != newCurrentItem) {

focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT;

// 取出上一个页面的当前信息

oldCurInfo = infoForPosition(mCurItem);

mCurItem = newCurrentItem;

}

if (mAdapter == null) {

sortChildDrawingOrder();

return;

}

// Bail now if we are waiting to populate. This is to hold off

// on creating views from the time the user releases their finger to

// fling to a new position until we have finished the scroll to

// that position, avoiding glitches from happening at that point.

// 如果正在填充,则排序子View的绘制顺序,保证绘制填充过程

if (mPopulatePending) {

if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");

sortChildDrawingOrder();

return;

}

// Also, don't populate until we are attached to a window. This is to

// avoid trying to populate before we have restored our view hierarchy

// state and conflicting with what is restored.

if (getWindowToken() == null) {

return;

}

// 调用mAdapter开始更新,将新的页面的数据更新

mAdapter.startUpdate(this);

// 计算缓存的偏移量,发生偏移后的区间的最大值和最小值position

final int pageLimit = mOffscreenPageLimit;

// 当前item的位置的最左边开始缓存的位置

final int startPos = Math.max(0, mCurItem - pageLimit);

final int N = mAdapter.getCount();// ViewPager的adapter的页面数量

// 当前item的位置最右边的缓存的位置

final int endPos = Math.min(N-1, mCurItem + pageLimit);

if (N != mExpectedAdapterCount) {

String resName;

try {

resName = getResources().getResourceName(getId());

} catch (Resources.NotFoundException e) {

resName = Integer.toHexString(getId());

}

throw new IllegalStateException("The application's PagerAdapter changed the adapter's" +

" contents without calling PagerAdapter#notifyDataSetChanged!" +

" Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N +

" Pager id: " + resName +

" Pager class: " + getClass() +

" Problematic adapter: " + mAdapter.getClass());

}

// Locate the currently focused item or add it if needed.

int curIndex = -1;

ItemInfo curItem = null;

// 遍历所有的缓存中的mItems,判断是否有新的mCurItem的数据已经在缓存中

// 如果有则取出

for (curIndex = 0; curIndex < mItems.size(); curIndex++) {

final ItemInfo ii = mItems.get(curIndex);

if (ii.position >= mCurItem) {

if (ii.position == mCurItem) curItem = ii;

break;

}

}

// 如果缓存中没有心的mCurItem对应的数据,则添加新的Item数据

// 创建一个Adapter的新的item

if (curItem == null && N > 0) {

curItem = addNewItem(mCurItem, curIndex);

}

// Fill 3x the available width or up to the number of offscreen

// pages requested to either side, whichever is larger.

// If we have no current item we have no work to do.

if (curItem != null) {

float extraWidthLeft = 0.f;

int itemIndex = curIndex - 1;

ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;

final int clientWidth = getPaddedWidth();

final float leftWidthNeeded = clientWidth <= 0 ? 0 :

2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;

// 这里的mCurItem是已经切换到了新的Fragment的index

// 这部分内容是对预加载的Fragment进行处理的。如果当前的pos值与开始

// 需要缓存的值相比,小于,则判断mItems中是否有存在不需要缓存的Fragment,

// 如果有,则删除,并且将mAdapter中的销毁。下面的endPos的也是同理。

// 如果是需要新加入缓存的,则通过调用addNewItem,将需要加入缓存的

// Fragment添加到ItemInfo对象中,然后添加到mItems中

for (int pos = mCurItem - 1; pos >= 0; pos--) {

if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {

if (ii == null) {

break;

}

if (pos == ii.position && !ii.scrolling) {

mItems.remove(itemIndex);

// 销毁item

// 一般调用container.removeView((View) object);

// 即从container中删除对应的object

// 这个container其实就是ViewPager

mAdapter.destroyItem(this, pos, ii.object);

if (DEBUG) {

Log.i(TAG, "populate() - destroyItem() with pos: " + pos +

" view: " + ii.object);

}

itemIndex--;

curIndex--;

ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;

}

} else if (ii != null && pos == ii.position) {

extraWidthLeft += ii.widthFactor;

itemIndex--;

ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;

} else {

ii = addNewItem(pos, itemIndex + 1);

extraWidthLeft += ii.widthFactor;

curIndex++;

ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;

}

}

float extraWidthRight = curItem.widthFactor;

itemIndex = curIndex + 1;

if (extraWidthRight < 2.f) {

ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;

final float rightWidthNeeded = clientWidth <= 0 ? 0 :

(float) getPaddingRight() / (float) clientWidth + 2.f;

for (int pos = mCurItem + 1; pos < N; pos++) {

if (extraWidthRight >= rightWidthNeeded && pos > endPos) {

if (ii == null) {

break;

}

if (pos == ii.position && !ii.scrolling) {

mItems.remove(itemIndex);

// 如果缓存中存在超出缓存界限的ItemInfo,则remove

// 并且调用Adapter.destroyItem销毁

mAdapter.destroyItem(this, pos, ii.object);

if (DEBUG) {

Log.i(TAG, "populate() - destroyItem() with pos: " + pos +

" view: " + ii.object);

}

ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;

}

} else if (ii != null && pos == ii.position) {

extraWidthRight += ii.widthFactor;

itemIndex++;

ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;

} else {

ii = addNewItem(pos, itemIndex);

itemIndex++;

extraWidthRight += ii.widthFactor;

ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;

}

}

}

calculatePageOffsets(curItem, curIndex, oldCurInfo);

}

if (DEBUG) {

Log.i(TAG, "Current page list:");

for (int i=0; i

Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);

}

}

// setPrimaryItem方法其实就是具体去调用对应的Fragment的显示隐藏的回调的

// 方法,在这里会先判断被选择的item是否是与当前的item一致,

// 如果被选择的fragment与当前的一致,则什么都不做,如果不一致,

// 则将当前的Fragment的显示隐藏的回调置为false,将新的被选择的Fragment

// 置为true,并且更新当前Fragment对象缓存

// 在创建item之后,设置给adapter

mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);

// 如果adapter需要更新新的item,那么在调用setPrimaryItem之后

// 就会调用finishUpdate结束更新

mAdapter.finishUpdate(this);

// Check width measurement of current pages and drawing sort order.

// Update LayoutParams as needed.

final int childCount = getChildCount();

for (int i = 0; i < childCount; i++) {

final View child = getChildAt(i);

final LayoutParams lp = (LayoutParams) child.getLayoutParams();

lp.childIndex = i;

if (!lp.isDecor && lp.widthFactor == 0.f) {

// 0 means requery the adapter for this, it doesn't have a valid width.

final ItemInfo ii = infoForChild(child);

if (ii != null) {

lp.widthFactor = ii.widthFactor;

lp.position = ii.position;

}

}

}

sortChildDrawingOrder();

if (hasFocus()) {

View currentFocused = findFocus();

ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;

if (ii == null || ii.position != mCurItem) {

for (int i=0; i

View child = getChildAt(i);

ii = infoForChild(child);

if (ii != null && ii.position == mCurItem) {

final Rect focusRect;

if (currentFocused == null) {

focusRect = null;

} else {

focusRect = mTempRect;

currentFocused.getFocusedRect(mTempRect);

offsetDescendantRectToMyCoords(currentFocused, mTempRect);

offsetRectIntoDescendantCoords(child, mTempRect);

}

if (child.requestFocus(focusDirection, focusRect)) {

break;

}

}

}

}

}

}

看下ViewPager.addNewItem的源码

其实ViewPager.addNewItem就是通过调用Adapter.instantiateItem来创建对应的View,并且将View保存到ItemInfo中的object属性,并且判断ViewPager缓存中是否已经有ItemInfo,如果没有,则添加,如果有则做修改替换

ItemInfo addNewItem(int position, int index) {

ItemInfo ii = new ItemInfo();

ii.position = position;

ii.object = mAdapter.instantiateItem(this, position);

ii.widthFactor = mAdapter.getPageWidth(position);

if (index < 0 || index >= mItems.size()) {

mItems.add(ii);

} else {

mItems.add(index, ii);

}

return ii;

}

四、FragmentStatePagerAdapter源码解析

@SuppressWarnings("deprecation")

public abstract class FragmentStatePagerAdapter extends PagerAdapter {

private static final String TAG = "FragmentStatePagerAdapt";

private static final boolean DEBUG = false;

@Retention(RetentionPolicy.SOURCE)

@IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})

private @interface Behavior { }

/**

* Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current

* fragment changes.

*

* @deprecated This behavior relies on the deprecated

* {@link Fragment#setUserVisibleHint(boolean)} API. Use

* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,

* {@link FragmentTransaction#setMaxLifecycle}.

* @see #FragmentStatePagerAdapter(FragmentManager, int)

*/

@Deprecated

public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;

/**

* Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}

* state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.

*

* @see #FragmentStatePagerAdapter(FragmentManager, int)

*/

public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;

private final FragmentManager mFragmentManager;

private final int mBehavior;

private FragmentTransaction mCurTransaction = null;

private ArrayList mSavedState = new ArrayList<>();

private ArrayList mFragments = new ArrayList<>();

private Fragment mCurrentPrimaryItem = null;

private boolean mExecutingFinishUpdate;

/**

* Constructor for {@link FragmentStatePagerAdapter} that sets the fragment manager for the

* adapter. This is the equivalent of calling

* {@link #FragmentStatePagerAdapter(FragmentManager, int)} and passing in

* {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}.

*

*

Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the

* current Fragment changes.

*

* @param fm fragment manager that will interact with this adapter

* @deprecated use {@link #FragmentStatePagerAdapter(FragmentManager, int)} with

* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}

*/

@Deprecated

public FragmentStatePagerAdapter(@NonNull FragmentManager fm) {

this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);

}

/**

* Constructor for {@link FragmentStatePagerAdapter}.

*

* If {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} is passed in, then only the current

* Fragment is in the {@link Lifecycle.State#RESUMED} state, while all other fragments are

* capped at {@link Lifecycle.State#STARTED}. If {@link #BEHAVIOR_SET_USER_VISIBLE_HINT} is

* passed, all fragments are in the {@link Lifecycle.State#RESUMED} state and there will be

* callbacks to {@link Fragment#setUserVisibleHint(boolean)}.

*

* @param fm fragment manager that will interact with this adapter

* @param behavior determines if only current fragments are in a resumed state

*/

public FragmentStatePagerAdapter(@NonNull FragmentManager fm,

@Behavior int behavior) {

mFragmentManager = fm;

mBehavior = behavior;

}

/**

* Return the Fragment associated with a specified position.

*/

@NonNull

public abstract Fragment getItem(int position);

@Override

public void startUpdate(@NonNull ViewGroup container) {

if (container.getId() == View.NO_ID) {

throw new IllegalStateException("ViewPager with adapter " + this

+ " requires a view id");

}

}

@SuppressWarnings("deprecation")

@NonNull

@Override

public Object instantiateItem(@NonNull ViewGroup container, int position) {

// If we already have this item instantiated, there is nothing

// to do. This can happen when we are restoring the entire pager

// from its saved state, where the fragment manager has already

// taken care of restoring the fragments we previously had instantiated.

if (mFragments.size() > position) {

Fragment f = mFragments.get(position);

if (f != null) {

return f;

}

}

if (mCurTransaction == null) {

mCurTransaction = mFragmentManager.beginTransaction();

}

Fragment fragment = getItem(position);

if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);

if (mSavedState.size() > position) {

Fragment.SavedState fss = mSavedState.get(position);

if (fss != null) {

fragment.setInitialSavedState(fss);

}

}

while (mFragments.size() <= position) {

mFragments.add(null);

}

// 调用fragment的对应的方法

// 比如setUserVisibleHint

// 此时将fragment添加到事务中,但是还没提交事务

// 优先调用了setUserVisibleHint,而提交事务是在finishUpdate中

// 只有提交事务了才会执行生命周期

fragment.setMenuVisibility(false);

if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {

fragment.setUserVisibleHint(false);

}

mFragments.set(position, fragment);

mCurTransaction.add(container.getId(), fragment);

if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);

}

return fragment;

}

// TODO(b/141958824): Suppressed during upgrade to AGP 3.6.

@SuppressWarnings("ReferenceEquality")

@Override

public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {

Fragment fragment = (Fragment) object;

if (mCurTransaction == null) {

mCurTransaction = mFragmentManager.beginTransaction();

}

if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object

+ " v=" + ((Fragment)object).getView());

while (mSavedState.size() <= position) {

mSavedState.add(null);

}

mSavedState.set(position, fragment.isAdded()

? mFragmentManager.saveFragmentInstanceState(fragment) : null);

mFragments.set(position, null);

mCurTransaction.remove(fragment);

if (fragment.equals(mCurrentPrimaryItem)) {

mCurrentPrimaryItem = null;

}

}

@Override

@SuppressWarnings({"ReferenceEquality", "deprecation"})

public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {

Fragment fragment = (Fragment)object;

if (fragment != mCurrentPrimaryItem) {

// 在切换ViewPager的时候,会调用adapter的setPrimaryItem方法

// 切换的时候,会将当前Fragment的setUserVisibleHint设置为false

// 会将目标fragment的setUserVisibleHint设置为true

// 即当前fragment需要隐藏,目标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);

if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

if (mCurTransaction == null) {

mCurTransaction = mFragmentManager.beginTransaction();

}

mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);

} else {

fragment.setUserVisibleHint(true);

}

mCurrentPrimaryItem = fragment;

}

}

@Override

public void finishUpdate(@NonNull ViewGroup container) {

if (mCurTransaction != null) {

// We drop any transactions that attempt to be committed

// from a re-entrant call to finishUpdate(). We need to

// do this as a workaround for Robolectric running measure/layout

// calls inline rather than allowing them to be posted

// as they would on a real device.

if (!mExecutingFinishUpdate) {

// 提交事务

// 在ViewPager.populate方法中调用

// 当执行完Adapter的startUpdate、addNewItem

// instantiateItem、destroyItem、setPrimaryItem

// 之后,才会调用finishUpdate

try {

mExecutingFinishUpdate = true;

mCurTransaction.commitNowAllowingStateLoss();

} finally {

mExecutingFinishUpdate = false;

}

}

mCurTransaction = null;

}

}

@Override

public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {

return ((Fragment)object).getView() == view;

}

@Override

@Nullable

public Parcelable saveState() {

Bundle state = null;

if (mSavedState.size() > 0) {

state = new Bundle();

Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];

mSavedState.toArray(fss);

state.putParcelableArray("states", fss);

}

for (int i=0; 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;

}

@Override

public void restoreState(@Nullable Parcelable state, @Nullable ClassLoader loader) {

if (state != null) {

Bundle bundle = (Bundle)state;

bundle.setClassLoader(loader);

Parcelable[] fss = bundle.getParcelableArray("states");

mSavedState.clear();

mFragments.clear();

if (fss != null) {

for (int i=0; i

mSavedState.add((Fragment.SavedState)fss[i]);

}

}

Iterable keys = bundle.keySet();

for (String key: keys) {

if (key.startsWith("f")) {

int index = Integer.parseInt(key.substring(1));

Fragment f = mFragmentManager.getFragment(bundle, key);

if (f != null) {

while (mFragments.size() <= index) {

mFragments.add(null);

}

f.setMenuVisibility(false);

mFragments.set(index, f);

} else {

Log.w(TAG, "Bad fragment at key " + key);

}

}

}

}

}

}

从分析FragmentStatePagerAdapter来看,setUserVisibleHint方法会优先于Fragment的生命周期函数 执行。因为在FragmentStatePagerAdapter中提交事务,是在调用finishUpdate方法中进行的,只有提交事务的时候,才会去执行Fragment的生命周期。

FragmentStatePagerAdapter中的instantiateItem和destroyItem都实现了对fragment的事务的添加和删除,而finishUpdate实现了事务的提交,所以在实现FragmentStatePagerAdapter的时候,并不需要重写instantiateItem和destroyItem

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值