001-ViewPager实现Fragment懒加载,相邻的界面Fragment方法getUserVisibleHint()返回了true,导致重新加载数据

2024-09-14 17:05 001-ViewPager实现Fragment懒加载,相邻的界面Fragment方法getUserVisibleHint()返回了true,导致重新加载数据已关闭评论

在使用 ViewPager 实现 Fragment 懒加载时,getUserVisibleHint() 方法通常用于判断 Fragment 是否可见,从而决定是否执行数据加载等操作。然而,有时候会发现即使 Fragment 并未完全进入可视状态,getUserVisibleHint() 也会返回 true,造成的原因包括:

  1. ViewPager 的预加载机制ViewPager 默认会预加载邻近的 Fragment。这意味着即使当前 Fragment 还没有进入可视范围,为了保证流畅的用户体验,ViewPager 也可能提前将其加载进来。这种情况下,getUserVisibleHint() 可能会提前返回 true

  2. 页面滑动过程中:在用户滑动 ViewPager 时,Fragment 可能在即将成为可见状态之前就收到了 true 的提示。这是因为 ViewPager 在计算可见性时可能会有一定的提前量,以确保页面切换时不会有明显的延迟感。

  3. 生命周期回调的不确定性:由于 Fragment 生命周期的复杂性,尤其是在 ViewPager 的上下文中,Fragment 的可见性变化可能会在不同的生命周期回调中触发,导致 getUserVisibleHint() 在某些非预期时刻返回 true

解决方案

为了更好地控制 Fragment 的懒加载行为,你可以采取以下措施:

  1. 结合 isResumed()isVisibleToUser():除了依赖 getUserVisibleHint(),还可以结合使用 isResumed() 方法,确保 Fragment 不仅是可见的而且已经恢复了状态。

  2. 使用 setOffscreenPageLimit():你可以通过设置 ViewPagersetOffscreenPageLimit() 方法来调整预加载的页面数量。如果预加载不是必需的,可以将此值设置为 1 或更少,从而减少提前加载的情况。

  3. 自定义懒加载逻辑:在 Fragment 中实现自己的懒加载逻辑,比如通过监听 ViewPager 的滚动事件来确定何时加载数据。这种方法更精确,但同时也增加了复杂性。

  4. 利用 Lifecycle 组件:如果使用的是 AndroidX 库,可以利用 Lifecycle 组件来更准确地控制 Fragment 的可见性,例如监听 Lifecycle.Event.ON_RESUMELifecycle.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作用

  1. 兼容性处理

    • 在较旧的 Android 版本中,setUserVisibleHint() 方法可能不存在或者行为不同。通过这个条件语句,可以确保只有在支持该方法的设备上才调用它,从而避免潜在的 NoSuchMethodError 或其他兼容性问题。
  2. 懒加载逻辑

    • setUserVisibleHint() 方法被调用时,通常意味着 Fragment 即将变得可见或者已经不可见。因此,在这个方法里设置 mIsVisibleToUser 的值,可以用来作为懒加载的一个依据。
    • 通过检查 setUserVisibleHint() 是否为 true,可以决定是否加载数据。
  3. 安全编程实践

    • 检查 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元,扫一扫支付后添加微信提供帮助!(如不能解决您的问题,可以申请退款)

你可能感兴趣的文章

来源:每日教程每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://teachcourse.cn/3673.html ,谢谢支持!

资源分享

ProgressBar+WebView实现自定义浏览器 ProgressBar+WebView实现自定
冰凉一夏天,尝尝自制冰冻绿豆冰吧 冰凉一夏天,尝尝自制冰冻绿豆冰
插入排序算法 插入排序算法
Android开发之深入理解Builder设计模式 Android开发之深入理解Builder

评论已关闭!