ViewPager 中使用 Fragment 时防止数据预加载,腾讯架构师深入讲解 Android 开发
*/
public void setUserVisibleHint(boolean isVisibleToUser) {
if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
&& mFragmentManager != null && isAdded()) {
mFragmentManager.performPendingDeferredStart(this);
}
mUserVisibleHint = isVisibleToUser;
mDeferStart = mState < STARTED && !isVisibleToUser;
}
以上是来自 support v4 包中的 Fragment 源码, 大概的意思是调用这个方法来设置 Fragment 当前是否是对用户可见的,这个方法只有一个参数,visible 就传 true,否则就传 false。可是在 Fragment 源码搜索发现并没有地方调用这个方法,那这个方法可能是给用户来调用的。
由此,我们可以猜想肯定是在 ViewPager 使用的过程中的某个地方调用了这个方法,我们看下 ViewPager 使用 Fragment 的流程一般是:
mAdapter = new BaseFragmentPagerAdapter(getSupportFragmentManager(), mFragmentList);
mViewPager.setAdapter(mAdapter);
于是,我先到 FragmentPagerAdapter 的源码中搜索了下,果不其然,发现了踪迹:
@SuppressWarnings("ReferenceEquality")
@Override
public Object instantiateItem(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) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
在 instantiateItem 方法的最后我们发现了调用的地方:
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
这里有个判断fragment != mCurrentPrimaryItem
这个时候会把 Fragment 的 Visible 设为 false, 那这个mCurrentPrimaryItem
又是什么呢,继续搜索源码:
@SuppressWarnings("ReferenceEquality")
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
在一个setPrimaryItem
的方法中找到这个变量的赋值,这个方法将传进来的 fragment 的 visible 设置为 true, 同时会更新 mCurrentPrimaryItem 变量的值。我们再继续搜索setPrimaryItem
这个方法的调用,结果在当前 FragmentPagerAdapter 的源码中没有找到,但是在它的父类 PagerAdapter 的源码中找到了它的定义:
/**
Called to inform the adapter of which item is currently considered to
be the "primary", that is the one show to the user as the current page.
@param container The containing View from which the page will be removed.
@param position The page position that is now the primary.
@param object The same object that was returned by
{@link #instantiateItem(View, int)}.
*/
public void setPrimaryItem(ViewGroup container, int position, Object object) {
setPrimaryItem((View) container, position, object);
}
看注释大概明白了这个方法的含义是设置为 ViewPager 中当前展示给用户的那一页。
继续到 ViewPager 的源码中搜索,找到了调用它的地方:
是在一个populate()
的方法中调用的,搜索发现好多地方调用了它,但是发现了有两个关键的地方:
/**
Set a PagerAdapter that will supply views for this pager as needed.
@param adapter Adapter to use
*/
public void setAdapter(PagerAdapter adapter) {
//...忽略部分源码
if (mAdapter != null) {
//...忽略部分源码
if (mRestoredCurItem >= 0) {
mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
setCurrentItemInternal(mRestoredCurItem, false, true);
mRestoredCurItem = -1;
mRestoredAdapterState = null;
mRestoredClassLoader = null;
} else if (!wasFirstLayout) {
populate();
} else {
requestLayout();
}
}
//...忽略部分源码
}
/**
Set the currently selected page.
@param item Item index to select
@param smoothScroll True to smoothly scroll to the new item, false to transition immediately
*/
public void setCurrentItem(int item, boolean smoothScroll) {
mPopulatePending = false;
setCurrentItemInternal(item, smoothScroll, false);
评论