Android应用开发过程中,ViewPager同时加载多个fragment,以实现多tab页面快速切换, 但是fragment初始化时若加载的内容较多,就可能导致整个应用启动速度缓慢,影响用户体验。
为了提高用户体验,我们会使用一些懒加载方案,实现加载延迟。这时我们会用到getUserVisibleHint()与setUserVisibleHint()这两个方法。
*
@param
isVisibleToUser true if this fragment's UI is currently visible to the user (default),
* false if it is not.
public
void
setUserVisibleHint
(
boolean
isVisibleToUser) {
if
(!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
mFragmentManager.performPendingDeferredStart(
this
);
mUserVisibleHint = isVisibleToUser;
mDeferStart = !isVisibleToUser;
*
@return
The current value of the user-visible hint on this fragment.
*
@see
#setUserVisibleHint(boolean)
public
boolean
getUserVisibleHint
() {
return
mUserVisibleHint;
从上述源码注释我们可以看出,当fragment被用户可见时,setUserVisibleHint()会调用且传入true值,当fragment不被用户可见时,setUserVisibleHint()则得到false值。而在传统的fragment生命周期里也看不到这个函数。
那么,问题来了,
-
fragment是如何知道自己时候用户可见?
-
setUserVisibleHint() 在上图所示fragment的生命周期的什么位置?
先说结论,
-
viewpager监听切换tab事件,tab切换一次,执行一次setUserVisibleHint()方法
-
setUserVisibleHint() 在 上图所示fragment所有生命周期之前,无论viewpager是在activity哪个生命周期里初始化。
-
activity生命周期 和 fragment生命周期 时序并不是按序来的,也就是说fragment的oncreate方法时序并不一定在activity的oncreate方法之后。
具体原因,我们从应用场景开始一点一点的分析。
Theme 1. 我们的应用场景
public class MainActivity extends FragmentActivity {
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewPager = (ViewPager) findViewById(R.id.viewpager);
vpOrder.setAdapter(new MainFragmentPagerAdapter(getSupportFragmentManager()));
vpOrder.setOffscreenPageLimit(5);
vpOrder.setCurrentItem(0);
- /frameworks/base/core/java/com/android/internal/widget/ViewPager.java
void populate(int newCurrentItem) {
mAdapter.startUpdate(this);
addNewItem(mCurItem, curIndex);
mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
mAdapter.finishUpdate(this);
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;
- /frameworks/support/v4/java/android/support/v4/app/FragmentPagerAdapter.java
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
final long itemId = getItemId(position);
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;
@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;
Theme 3. Activity , FragmentManager
- /frameworks/base/core/java/android/app/Activity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
mCalled = true;
- /frameworks/support/v4/java/android/support/v4/app/FragmentManager.java
public void dispatchCreate() {
mStateSaved = false;
moveToState(Fragment.CREATED, false);
void moveToState(int newState, boolean always) {
moveToState(newState, 0, 0, always);
void moveToState(int newState, int transit, int transitStyle, boolean always) {
if (mHost == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No host");
if (!always && mCurState == newState) {
return;
mCurState = newState;
if (mActive != null) {
boolean loadersRunning = false;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null) {
moveToState(f, newState, transit, transitStyle, false);
if (f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
if (!loadersRunning) {
startPendingDeferredFragments();
if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
mHost.onSupportInvalidateOptionsMenu();
mNeedMenuInvalidate = false;
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
f.onAttach();
f.performOnCreate();
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
if (DEBUG) Log.v(TAG, "add: " + fragment);
makeActive(fragment);
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
if (moveToStateNow) {
moveToState(fragment);
void makeActive(Fragment f) {
if (f.mIndex >= 0) {
return;
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) {
mActive = new ArrayList<Fragment>();
f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
一开始不知道这个方法,用到tablayout+ viewpager,viewpager里面包含的是fragment,有这样的需求就是,每次滑动需要刷新当前页面,百度一看,有两个方法可以实现,1.setUserVisibleHint 2.onHiddenChanged
如果使用的是上面这张viewpager+tablayout 这种方法,用setUserVisibleHint可以有效的实现刷新数据...
现在越来越多的应用会使用viewpager+fragment显示自己的内容页,fragment和activity有很多共同点,如下图就是fragment的生命周期
但是fragment和activity不同的是当调用本身的onResume和onPause方法的时候可能并不是当前的fragment在显示,例如当加载下面这张图时,当我打开MainActivity时显示的是第一个fragment
* Created by xz on 2017/4/10.
public abstract class BaseFragment extends Fragment { /** 是否第一次显示 */
private boolean isFirstVisible = true ;
/** 是否第一次不显示 */
private boolean isFirs
Fragment的懒加载与生命周期详解什么是懒加载了解Fragment的生命周期onAttachonCreateonCreateViewonActivityCreatedonStartonResumeonPauseonStoponDestroyViewonDestroyonDetach懒加载的实现和封装
什么是懒加载
在Android中经常用到ViewPager + Fragment来加载数据,但...
androidx下的
fragment中onHiddenChanged方法不会被调用,这就导致了懒加载实现方式要变更
前置条件:
BottomNavigation
View+
ViewPager+
fragment
FragmentPagerAdapter
pagerAdapter = new
FragmentPagerAdapter(getSupport
FragmentManager(),
FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_
FRAGMENT)
在写Android项目过程中,不可避免的会遇到用Fragment当作一个界面,尤其是当使用ViewPager 配合使用Fragment,更是头疼,我就是遇到了这个问题,所以大概的研究了一下,来给大家分享一下!
1.项目中实现ViewPager 和PagerSlidingTabStrip配合使用,为了使滑动流畅,就是ViewPager.setOffScreenPageLimit(),实现页面预加载
话说最近项目遇到点问题,就是类似网易的那种tab切换fragment,但是每次fagment都会遇到下面几个问题:要么这些fragment同时初始化,要么就是刚加载好的fragment滑出界面再滑回来又重新加载了。如果你遇到这样的问题,请继续看。
项目最开始的框架源码使用的是这个帖子的:仿网易新闻客户端的导航效果,在此谢谢原作者,在你的代码上做了些许改动,还望见谅。废话少说,效果见