在使用 ViewPager 实现 Fragment 懒加载时,getUserVisibleHint() 方法通常用于判断 Fragment 是否可见,从而决定是否执行数据加载等操作。然而,有时候会发现即使 Fragment 并未完全进入可视状态,getUserVisibleHint() 也会返回 true,造成的原因包括:
-
ViewPager 的预加载机制:
ViewPager默认会预加载邻近的Fragment。这意味着即使当前Fragment还没有进入可视范围,为了保证流畅的用户体验,ViewPager也可能提前将其加载进来。这种情况下,getUserVisibleHint()可能会提前返回true。 -
页面滑动过程中:在用户滑动
ViewPager时,Fragment可能在即将成为可见状态之前就收到了true的提示。这是因为ViewPager在计算可见性时可能会有一定的提前量,以确保页面切换时不会有明显的延迟感。 -
生命周期回调的不确定性:由于
Fragment生命周期的复杂性,尤其是在ViewPager的上下文中,Fragment的可见性变化可能会在不同的生命周期回调中触发,导致getUserVisibleHint()在某些非预期时刻返回true。
解决方案
为了更好地控制 Fragment 的懒加载行为,你可以采取以下措施:
-
结合
isResumed()和isVisibleToUser():除了依赖getUserVisibleHint(),还可以结合使用isResumed()方法,确保Fragment不仅是可见的而且已经恢复了状态。 -
使用
setOffscreenPageLimit():你可以通过设置ViewPager的setOffscreenPageLimit()方法来调整预加载的页面数量。如果预加载不是必需的,可以将此值设置为1或更少,从而减少提前加载的情况。 -
自定义懒加载逻辑:在
Fragment中实现自己的懒加载逻辑,比如通过监听ViewPager的滚动事件来确定何时加载数据。这种方法更精确,但同时也增加了复杂性。 -
利用
Lifecycle组件:如果使用的是 AndroidX 库,可以利用Lifecycle组件来更准确地控制Fragment的可见性,例如监听Lifecycle.Event.ON_RESUME和Lifecycle.Event.ON_PAUSE事件。
封装示例
一个简单的示例,展示了如何在 Fragment 中实现懒加载:
public class LazyFragment extends Fragment {
private boolean mIsVisibleToUser = false;
private boolean mIsResumed = false;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mIsVisibleToUser = isVisibleToUser;
lazyLoad();
}
}
@Override
public void onResume() {
super.onResume();
mIsResumed = true;
lazyLoad();
}
@Override
public void onPause() {
super.onPause();
mIsResumed = false;
}
private void lazyLoad() {
if (mIsVisibleToUser && mIsResumed) {
// 在这里执行你的数据加载逻辑
loadData();
}
}
private void loadData() {
// 实际的数据加载操作
}
}
setUserVisibleHint() 方法
在提供的示例代码中,if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) 这一行的作用是检查当前运行的 Android 系统版本是否等于或高于 Android 4.2 (Jelly Bean MR1),即 API 级别 17。
这段代码的目的是为了确保在支持 setUserVisibleHint() 方法的设备上才去调用它。setUserVisibleHint() 方法是在 Android 4.2 版本引入的,所以在低于该版本的设备上,这个方法不会存在,直接调用可能会导致运行时错误或者行为不符合预期。
这个句话:Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1作用
-
兼容性处理:
- 在较旧的 Android 版本中,
setUserVisibleHint()方法可能不存在或者行为不同。通过这个条件语句,可以确保只有在支持该方法的设备上才调用它,从而避免潜在的NoSuchMethodError或其他兼容性问题。
- 在较旧的 Android 版本中,
-
懒加载逻辑:
- 当
setUserVisibleHint()方法被调用时,通常意味着Fragment即将变得可见或者已经不可见。因此,在这个方法里设置mIsVisibleToUser的值,可以用来作为懒加载的一个依据。 - 通过检查
setUserVisibleHint()是否为true,可以决定是否加载数据。
- 当
-
安全编程实践:
- 检查 API 版本是一种常用的编程技巧,用来确保使用的 API 在当前环境下可用。这样做可以提高应用的健壮性和兼容性。
完整的Fragment懒加载封装
public abstract class BaseLazyFragment extends V4BaseFragment {
/*
* 预加载页面回调的生命周期流程:
* setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()-->
* onActivityCreate() --> onStart() --> onResume()
*/
/**
* 懒加载过
*/
protected boolean isLazyLoaded = false;
/**
* Fragment的View加载完毕的标记
*/
private boolean isPrepared = false;
private boolean mIsResumed = false;
/**
* 第一步,改变isPrepared标记
* 当onViewCreated()方法执行时,表明View已经加载完毕,此时改变isPrepared标记为true,并调用lazyLoad()方法
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isPrepared = true;
//只有Fragment onCreateView好了
//另外这里调用一次lazyLoad()
lazyLoad();
}
@Override
public void onResume() {
super.onResume();
mIsResumed = true;
lazyLoad();
}
@Override
public void onPause() {
super.onPause();
mIsResumed=false;
}
/**
* 第二步
* 此方法会在onCreateView()之前执行
* 当viewPager中fragment改变可见状态时也会调用
* 当fragment 从可见到不见,或者从不可见切换到可见,都会调用此方法
* true表示当前页面可见,false表示不可见
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
DzLog.debug("setUserVisibleHint---" + isVisibleToUser);
//只有当fragment可见时,才进行加载数据
if (isVisibleToUser) {
lazyLoad();
}
}
/**
* 调用懒加载
* 第三步:在lazyLoad()方法中进行双重标记判断,通过后即可进行数据加载
*/
private void lazyLoad() {
DzLog.debug("当前ClassName:"+getClass().getName());
if (getUserVisibleHint() && isPrepared && !isLazyLoaded) {
onFirstLoading();
isLazyLoaded = true;
DzLog.debug("showFirstLoading---");
} else if (getUserVisibleHint() && isPrepared && mIsResumed) {
onLazyLoad();
DzLog.debug("lazyLoad---");
} else {
//当视图已经对用户不可见并且加载过数据,如果需要在切换到其他页面时停止加载数据,可以覆写此方法
if (isLazyLoaded) {
onStopLoading();
DzLog.debug("stopLoad---");
}
}
}
/**
* 视图销毁的时候讲Fragment是否初始化的状态变为false
*/
@Override
public void onDestroyView() {
super.onDestroyView();
isLazyLoaded = false;
isPrepared = false;
mIsResumed=false;
}
/**
* 第一次可见时,操作该方法,可以用于showLoading操作,注意这个是全局加载loading
*/
protected void onFirstLoading() {
DzLog.debug("第一次可见时show全局loading");
onLazyLoad();
}
/**
* 停止加载
* 当视图已经对用户不可见并且加载过数据,但是没有加载完,而只是加载loading。
* 如果需要在切换到其他页面时停止加载数据,可以覆写此方法。
* 存在问题,如何停止加载网络
*/
protected void onStopLoading() {
}
/**
* 第四步:定义抽象方法onLazyLoad(),具体加载数据的工作,交给子类去完成
*/
@UiThread
protected abstract void onLazyLoad();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
super.onCreateView(inflater,container,state);
return mViewGroup;
}
@Override
public void initFragment(Bundle state) {
}
@Override
public void initListener() {
}
}
当前文章价值5.4元,扫一扫支付后添加微信提供帮助!(如不能解决您的问题,可以申请退款)

评论已关闭!