ViewPager 预加载机制与 Fragment 懒加载实战:从 setOffscreenPageLimit 原理到 BaseLazyFragment 完整实现

发布时间:2026/10/9 4:41:23
ViewPager 预加载机制与 Fragment 懒加载实战:从 setOffscreenPageLimit 原理到 BaseLazyFragment 完整实现 教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载导读本篇文章基于 YCBlogs 仓库中的《03.ViewPager懒加载.md》一文展开系统梳理 ViewPager 的预加载机制、setOffscreenPageLimit的源码限制、Fragment 懒加载的核心思想并给出可直接落地的BaseLazyFragment与配合状态管理器的BaseStateFragment完整实现。阅读完本文你将理解 ViewPager 与 PagerAdapter 的协作流程掌握可见才加载、仅加载一次的懒加载方案并能在项目里快速复用这套封装解决多 Fragment 同时发网络请求导致流量浪费、页面卡顿的经典问题。01. ViewPager 与 PagerAdapter 的协作机制ViewPager 并不直接管理页面 View而是使用一个键key对象来关联每一页。这个键用于追踪并唯一标识 Adapter 中独立位置的某一页。整个协作过程依赖 PagerAdapter 的四个核心回调ViewPager 通过回调方式通知 PagerAdapter 管理其中的页面startUpdate(ViewGroup)表明 ViewPager 中的内容需要更改一次更新的开始。instantiateItem(ViewGroup, int)构造页面视图。通过一次或多次调用该方法来构造需要的页面。destroyItem(ViewGroup, int, Object)取消 ViewPager 关联的页面视图即销毁不再需要的页面。finishUpdate(ViewGroup)当一次更新添加和/或移除完成之后调用通知 Adapter 提交关联和/或取消关联的操作。最简单的实现方式是用每页的 View 本身作为 key 来关联它们自己在instantiateItem(ViewGroup, int)中创建 View 并添加到 ViewGroup 后返回该 ViewdestroyItem(ViewGroup, int, Object)中将其从 ViewGroup 移除同时在isViewFromObject(View, Object)中返回return view object;。此外PagerAdapter 支持数据改变时刷新界面。数据改变必须在主线程中调用并在改变完成后调用notifyDataSetChanged()这与AdapterView中派生自BaseAdapter的用法相似。一次数据改变可能关联页面的添加、移除或位置改变ViewPager 会根据 Adapter 中getItemPosition(Object)返回的结果判断是否保留当前已构造的活动页面重用而非完全重新构造。关于 PagerAdapter 更完整的抽象方法说明instantiateItem、destroyItem、isViewFromObject、getItemPosition等与 ViewPager 的配合关系可进一步阅读仓库中的 PagerAdapter详细介绍。02. ViewPager 弊端分析为什么需要懒加载普通 ViewPager 如果你不通过setOffscreenPageLimit(int limit)显式设置预加载数量默认会加载当前页的左右两页。也就是说进入 ViewPager 第一页时第一页和第二页会被一起加载。这带来一个非常现实的问题如果我们把setOffscreenPageLimit设置为 3那么进入 ViewPager 以后会同时加载 4 个 Fragment。像平时项目中的这些 Fragment 一般都会发送网络请求也就是说 4 个 Fragment 同时发网络请求去获取数据。结果显而易见——用户体验不好比如浪费用户流量、造成卡顿等等。而懒加载针对 Fragment 实现 PagerAdapter 时还有更深层的矛盾概念上懒加载是当需要时才加载加载之后一直保持该对象。但FragmentPagerAdapter与FragmentStatePagerAdapter都没有完全保存其引用和状态前者需要重建视图后者使用状态恢复View 都被销毁只是恢复方式不同。而我们通常希望 Fragment 一旦被加载其视图也不再被销毁即不重新走一遍完整生命周期。同时 ViewPager 为了实现滑动效果必然预加载左右两侧的页面。因此我们通常想要实现的两种效果是不提供滑动时需要时才构造页面并且只走一遍生命周期避免在 Fragment 中做过多的状态保存和恢复。这种预加载带来的可见性问题在真实场景中很常见。仓库中的仿抖音滑动分页视频一文就明确指出很多人以为 Fragment 在onResume的时候就是可见的但 ViewPager 中的 Fragment 恰恰相反尤其是多个 ViewPager 嵌套时会同时有多个父 Fragment、多个子 Fragment 处于onResume状态却只有其中一个是真正可见的。在页面内容曝光等重要的数据上报场景中就需要同时判断onResumed、setUserVisibleHint、setOnPageChangeListener等多个条件。03. ViewPager 预加载机制setOffscreenPageLimit 为什么设 0 无效既然预加载带来问题一个直观的想法是把 ViewPager 的预加载数量设为 0 不就行了代码这样写vp.setOffscreenPageLimit(0);但看一下 ViewPager 的源码就会明白这个方法是行不通的public void setOffscreenPageLimit(int limit) { if (limit 1) { Log.w(ViewPager, Requested offscreen page limit limit too small; defaulting to 1); limit 1; } if (limit ! this.mOffscreenPageLimit) { this.mOffscreenPageLimit limit; this.populate(); } }源码逻辑非常清晰即使你设置为 0方法内部也会判断后把它默认设为 1所以完全关闭预加载是不可能的。同时可以看到只有当新值与旧的mOffscreenPageLimit不同时才会触发populate()重新计算并布局页面。ViewPager 默认情况下的加载行为是当切换到当前页面时会默认预加载左右两侧的布局到 ViewPager 中尽管两侧的 View 并不可见。由于mOffscreenPageLimit的下限被限制为 1页面的预加载是不可避免的。以下以mOffscreenPageLimit 1为例说明缓存过程初始化缓存初始显示第 0 页mOffscreenPageLimit为 1因此预加载第 1 页再往后的页面第 2、3、4 页不需要加载。中间页面缓存向右滑动到第 2 页时左右各需要缓存一页此时第 0 页超出范围需要销毁第 3 页需要预加载第 4 页不需要加载。这也是为什么在实际项目中通常显式设置vp.setOffscreenPageLimit(1)如仿抖音滑动分页视频中的用法在保证滑动流畅的同时把预加载范围控制到最小。04. ViewPager 部分源码页面加载与销毁的核心流程理解懒加载的关键在于理解 ViewPager 内部是如何决定何时构造页面、何时销毁页面的。以下是文档梳理的核心方法04.1 setAdapter(ViewPager)设置 Adapter 时主要做这几件事销毁旧的 Adapter 数据用新的 Adapter 更新 UI清除旧 Adapter对已加载的 item 调用destroyItem将自身滚动到初始位置this.scrollTo(0, 0)设置 PagerObservermAdapter.setViewPagerObserver(mObserver)调用populate()方法计算并初始化 View如果设置了OnAdapterChangeListener进行回调。04.2 populate(int newCurrentItem)这是 ViewPager 中非常重要的方法主要根据参数newCurrentItem和mOffscreenPageLimit计算出需要初始化的页面和需要销毁的页面然后通过调用 Adapter 的instantiateItem和destroyItem两个方法初始化新页面、销毁不需要的页面。流程如下根据newCurrentItem和mOffscreenPageLimit计算要加载的 page 页面计算出startPos和endPos根据startPos和endPos初始化页面ItemInfo先从缓存里获取没有就调用addNewItem方法实际调用mAdapter.instantiateItem将不需要的ItemInfo移除mItems.remove(itemIndex)并调用mAdapter.destroyItem方法设置LayoutParams参数包括 position 和 widthFactor根据 position 排序待绘制 View 列表mDrawingOrderedChildren并重写getChildDrawingOrder方法最后一步获取当前显示 View 的焦点currView.requestFocus(View.FOCUS_FORWARD)。04.3 dataSetChanged()当调用 Adapter 的notifyDataSetChanged()时会触发这个方法重新计算当前页面的 position并刷新页面移除需要销毁的页面的ItemInfo对象然后再调用populate方法刷新页面循环mItems每个 page 对应的ItemInfo对象调用int newPos mAdapter.getItemPosition方法当newPos等于PagerAdapter.POSITION_UNCHANGED表示当前页面不需要更新、不用销毁当newPos等于PagerAdapter.POSITION_NONE表示需要更新移除 item 并调用mAdapter.destroyItem循环完成后最后计算出显示页面的newCurrItem调用setCurrentItemInternal(newCurrItem, false, true)方法更新 UI实际调用populate方法重新计算页面信息。04.4 scrollToItem 与 calculatePageOffsetsscrollToItem(int item, boolean smoothScroll, int velocity, boolean dispatchSelected)滑动到指定页面内部会触发OnPageChangeListenercalculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo)主要用于计算每个页面对应ItemInfo的 offset 变量。该变量记录当前 View 在所有缓存 View包含当前显示页中的索引用于布局时计算该 View 应该放在哪个位置。在populate方法中更新完页面数据后会调用该方法计算所有页面的 offset。从这套流程可以看出ViewPager 完全通过mOffscreenPageLimit划定了构造/销毁的边界Fragment 层面能做到的优化就是延迟真正耗时的初始化发网络请求到用户可见的那一刻。05. 懒加载出现问题setUserVisibleHint 的时机陷阱发现 Fragment 中有一个setUserVisibleHint(boolean isVisibleToUser)方法这个方法就是告诉用户 UI 对用户是否可见可以用来做懒加载初始化操作。但直接使用它有几个容易踩坑的地方因为 ViewPager 会加载多个 Fragment为了节省内存会在 Fragment 不可见的某个时候调用onDestroyView()销毁用户界面但 Fragment 实例还在。所以第一次加载可能没有问题但是再次回到第一个 Fragment 再去加载时会出现UI 对用户可见但视图还没有初始化的问题。懒加载需要处理的几个问题预加载虽然没有显示在界面上但当前页面的上一页和下一页的 Fragment 已经执行了一个 Fragment 能显示在界面上的所有生命周期方法。我们想做到跳转到该页时才真正构造数据视图和请求数据那么可以使用一个占位视图——ViewStub。当真正跳转到该页时执行ViewStub.inflate()方法加载真正的数据视图和请求数据。视图保存当某一页超出可视范围和预加载范围它将会被销毁。FragmentStatePagerAdapter销毁整个 Fragment可以自己保存该 Fragment或使用FragmentPagerAdapter让 FragmentManager 保留 Fragment 的引用。虽然这样它的生命周期方法已经走完只能手动保存 Fragment 根 View 的引用当再次重新进入新的生命周期方法时返回原来的 View。是否已经被用户所看到其实 FragmentManager 本身并没有提供 Fragment 被用户看到的回调方法而是在FragmentPagerAdapter和FragmentStatePagerAdapter中调用了Fragment.setUserVisibleHint(boolean)来表明 Fragment 是否已经被作为 primaryFragment。所以这个方法可以被认为是一个回调方法。关于两种 Fragment 适配器在销毁与恢复上的差异其源码实现见仓库中的 PagerAdapter详细介绍FragmentPagerAdapter.destroyItem内部调用mCurTransaction.detach((Fragment)object)只是 detach 而非 remove销毁了 Fragment 的视图但没有移除 Fragment 本身实例始终保留在内存中重新可见时只需 attach 即可恢复视图。FragmentStatePagerAdapter.destroyItem内部调用mCurTransaction.remove(fragment)同时通过mFragmentManager.saveFragmentInstanceState(fragment)缓存 Fragment 的状态真正移除 Fragment重新构造时会判断mSavedState中是否缓存了状态若有则通过setInitialSavedState恢复。这也对应了三种 Adapter 的缓存策略PagerAdapter缓存三个页面通过重写instantiateItem和destroyItem达到创建和销毁 View 的目的FragmentPagerAdapter内部通过 FragmentManager 持久化每一个 FragmentdestroyItem 时只是 detach并没有真正移除FragmentStatePagerAdapter内部通过 FragmentManager 管理每一个 FragmentdestroyItem 时真正移除。使用场景上PagerAdapter适合视图比较简单的情形FragmentPagerAdapter适合 Fragment 数量较少的页面如 3、4 个 Tab 的主页FragmentStatePagerAdapter适合条目数量特别多的场景如仿抖音分页视频见仿抖音滑动分页视频。06. 如何实现预加载机制BaseLazyFragment 完整代码剖析核心方法是 Fragment 中的setUserVisibleHint()此方法会在onCreateView()之前执行当 ViewPager 中 Fragment 改变可见状态时也会调用。当 Fragment 从可见到不可见、或者从不可见切换到可见时都会调用此方法使用getUserVisibleHint()可以返回 Fragment 当前是否可见的状态。在BaseLazyFragment中需要在onActivityCreated()及setUserVisibleHint()方法中都调用一次lazyLoad()方法。如果仅仅在setUserVisibleHint()里调用lazyLoad()当默认首页首先加载时会导致 ViewPager 的首页第一次展示时没有数据显示切换一下才会有数据。原因在于首页 Fragment 的setUserVisibleHint()在onActivityCreated()之前调用此时isPrepared为 false导致首页 Fragment 没能调用onLazyLoad()方法加载数据。完整实现如下/** * pre * author yangchong * time : 2017/7/22 * desc : 懒加载 * revise: 懒加载时机onCreateView()方法执行完毕 setUserVisibleHint()方法返回true * /pre */ public abstract class BaseLazyFragment extends BaseFragment { /* * 预加载页面回调的生命周期流程 * setUserVisibleHint() --onAttach() -- onCreate()--onCreateView()-- * onActivityCreate() -- onStart() -- onResume() */ /** * 懒加载过 */ protected boolean isLazyLoaded false; /** * Fragment的View加载完毕的标记 */ private boolean isPrepared false; /** * 第一步,改变isPrepared标记 * 当onViewCreated()方法执行时,表明View已经加载完毕,此时改变isPrepared标记为true,并调用lazyLoad()方法 */ Override public void onActivityCreated(Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); isPrepared true; //只有Fragment onCreateView好了 //另外这里调用一次lazyLoad( lazyLoad(); } /** * 第二步 * 此方法会在onCreateView(之前执行 * 当viewPager中fragment改变可见状态时也会调用 * 当fragment 从可见到不见或者从不可见切换到可见都会调用此方法 * true表示当前页面可见false表示不可见 */ Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); LogUtil.d(setUserVisibleHint---isVisibleToUser); //只有当fragment可见时才进行加载数据 if (isVisibleToUser){ lazyLoad(); } } /** * 调用懒加载 * 第三步:在lazyLoad()方法中进行双重标记判断,通过后即可进行数据加载 */ private void lazyLoad() { if (getUserVisibleHint() isPrepared !isLazyLoaded) { showFirstLoading(); onLazyLoad(); isLazyLoaded true; } else { //当视图已经对用户不可见并且加载过数据如果需要在切换到其他页面时停止加载数据可以覆写此方法 if (isLazyLoaded) { stopLoad(); } } } /** * 视图销毁的时候讲Fragment是否初始化的状态变为false */ Override public void onDestroyView() { super.onDestroyView(); isLazyLoaded false; isPrepared false; } /** * 第一次可见时操作该方法可以用于showLoading操作注意这个是全局加载loading */ protected void showFirstLoading() { LogUtil.i(第一次可见时show全局loading); } /** * 停止加载 * 当视图已经对用户不可见并且加载过数据但是没有加载完而只是加载loading。 * 如果需要在切换到其他页面时停止加载数据可以覆写此方法。 * 存在问题如何停止加载网络 */ protected void stopLoad(){ } /** * 第四步:定义抽象方法onLazyLoad(),具体加载数据的工作,交给子类去完成 */ UiThread protected abstract void onLazyLoad(); }06.1 onLazyLoad() 加载数据的三个条件这套实现之所以能正确工作依赖三个标记的联合判断getUserVisibleHint()返回 Fragment 是否可见状态这是 Fragment 实现懒加载的关键只有 Fragment 可见才会调用onLazyLoad()加载数据isPrepared在系统调用onActivityCreated时设置为 true此时onCreateView已调用完毕一般我们在这个方法里执行findViewById等操作确保onLazyLoad()方法不会报空指针异常isLazyLoaded确保 ViewPager 来回切换时initData方法不会被重复调用onLazyLoad在该 Fragment 的整个生命周期只调用一次第一次调用onLazyLoad()方法后马上执行isLazyLoaded true。然后继承这个BaseLazyFragment实现onLazyLoad()方法即可框架会自动控制在 Fragment 展现出来时才加载数据。06.2 几个值得优化的细节停止加载当视图已经对用户不可见且加载过数据时如果需要在切换到其他页面时停止加载数据可以覆写stopLoad方法。文档也坦诚指出其存在问题如何停止加载网络——停止网络请求本身需要配合请求库的取消机制才能彻底实现。状态重置视图销毁时把 Fragment 是否初始化的状态变为 falseonDestroyView中isLazyLoaded false; isPrepared false;这样重新进入时才能再次懒加载。首次 Loading 与局部刷新分离第一次可见时定义一个showFirstLoading方法用于全局 Loading 加载操作。需要注意它和下拉刷新数据或局部刷新的 loading 不一样——可能有些开发 App 没有将 loading 分得这么细。06.3 占位视图 ViewStub 的配合文档中提到使用一个占位视图当真正跳转到该页时才真正构造数据视图和请求数据这正是ViewStub的典型用法。ViewStub 是一个看不见、没有大小、不占布局位置的 View专门用于布局懒加载当调用inflate()或setVisibility(int)时布局才会被真正加载并替换掉 ViewStub。其构造方法中初始状态为setVisibility(GONE)、setWillNotDraw(true)因此不会参与绘制也不占空间。inflate()内部通过LayoutInflater加载目标布局、计算 ViewStub 在父布局中的 index然后把 ViewStub 移除、把新布局插入到相同位置详见仓库中的 ViewStub源码分析。用它作为懒加载页面的占位视图可以进一步减少首屏绘制负担。07. 懒加载配合状态管理器BaseStateFragment 完整实现07.1 什么是状态管理器一般在需要用户等待的场景显示一个 Loading 动画可以让用户知道 App 正在加载数据而不是程序卡死从而给用户较好的使用体验。进一步地当加载的数据为空时显示一个数据为空的视图数据加载失败时显示加载失败对应的 UI 并支持点击重试比白屏的用户体验更好加载中、加载失败、空数据的 UI 风格在 App 内所有页面中需要保持一致也就是需要做到全局统一。07.2 如何降低耦合性和入侵性为了让 View 状态的切换和 Activity/Fragment 彻底分离开需要把这些状态 View 都封装到一个管理类中然后暴露几个方法来实现 View 之间的切换。由于不同项目需要的状态 View 不一样管理类适合设计成 builder 模式来自由添加需要的状态 View。一个低耦合、低入侵、易维护、易移植的状态管理方案大致应具备以下条件可以运用在 Activity 或者 Fragment 中不需要在布局中添加 LoadingView而是统一管理不同状态视图同时暴露对外设置自定义状态视图的方法方便 UI 特定页面定制支持设置自定义不同状态视图即使在 BaseActivity 统一处理状态视图管理也支持单个页面定制加载视图时异常和空页面能否用ViewStub代替这样减少绘制只有等到出现异常和空页面时才将视图 inflate 出来当页面出现网络异常页、空页面时页面会有交互事件这时候可以设置点击网络或点击重新加载等。07.3 BaseStateFragment 完整代码具体操作上可以自由切换内容、空数据、异常错误、加载、网络错误等 5 种状态。父类BaseStateFragment直接暴露 5 种状态方便子类统一管理状态切换Fragment 的封装和 Activity 差不多/** * pre * author yangchong * time : 2017/7/20 * desc : fragment的父类 * revise: 注意该类具有懒加载 * /pre */ public abstract class BaseStateFragment extends BaseLazyFragment { protected StateLayoutManager statusLayoutManager; private View view; Nullable Override public View onCreateView(NonNull LayoutInflater inflater, Nullable ViewGroup container, Nullable Bundle savedInstanceState) { if(viewnull){ view inflater.inflate(R.layout.base_state_view, container , false); initStatusLayout(); initBaseView(view); } return view; } Override public void onViewCreated(NonNull View view, Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initView(view); initListener(); } Override public void onActivityCreated(Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); } /** * 获取到子布局 * param view view */ private void initBaseView(View view) { LinearLayout llStateView view.findViewById(R.id.ll_state_view); llStateView.addView(statusLayoutManager.getRootLayout()); } /** * 初始化状态管理器相关操作 */ protected abstract void initStatusLayout(); /** * 初始化View的代码写在这个方法中 * param view view */ public abstract void initView(View view); /** * 初始化监听器的代码写在这个方法中 */ public abstract void initListener(); /** * 第一次可见状态时showLoading操作注意下拉刷新操作时不要用该全局loading */ Override protected void showFirstLoading() { super.showFirstLoading(); showLoading(); } /*protected void initStatusLayout() { statusLayoutManager StateLayoutManager.newBuilder(activity) .contentView(R.layout.common_fragment_list) .emptyDataView(R.layout.view_custom_empty_data) .errorView(R.layout.view_custom_data_error) .loadingView(R.layout.view_custom_loading_data) .netWorkErrorView(R.layout.view_custom_network_error) .build(); }*/ /*---------------------------------下面是状态切换方法-----------------------------------------*/ /** * 加载成功 */ protected void showContent() { if (statusLayoutManager!null){ statusLayoutManager.showContent(); } } /** * 加载无数据 */ protected void showEmptyData() { if (statusLayoutManager!null){ statusLayoutManager.showEmptyData(); } } /** * 加载异常 */ protected void showError() { if (statusLayoutManager!null){ statusLayoutManager.showError(); } } /** * 加载网络异常 */ protected void showNetWorkError() { if (statusLayoutManager!null){ statusLayoutManager.showNetWorkError(); } } /** * 加载loading */ protected void showLoading() { if (statusLayoutManager!null){ statusLayoutManager.showLoading(); } } }07.4 如何切换状态子类在拿到数据后直接调用对应方法即可切换全局状态视图showContent(); showEmptyData(); showError(); showLoading(); showNetWorkError(); //或者这样操作也可以 statusLayoutManager.showLoading(); statusLayoutManager.showContent();07.5 状态管理器的设计思路状态管理器整体由三个核心部分组成StateFrameLayout继承 FrameLayout 的自定义布局主要作用是存放不同的状态视图以及隐藏和展示视图的操作StateLayoutManager状态管理器主要是让开发者设置不同状态视图的 view以及切换视图状态的操作。设计要点是loading 和内容 View 在界面状态切换中一直需要加载显示而空数据、异常、网络错误这 3 种状态只有在没数据或者网络异常的情况下才会加载显示所以用ViewStub来加载它们可以提高性能延迟 inflate、减少首帧绘制OnRetryListener一个接口主要作用是重试。比如加载失败了点击视图需要重新刷新接口就可以用到它开发者也可以自己设置点击事件。BaseStateFragment与BaseLazyFragment的组合是这套方案的精华BaseStateFragment继承BaseLazyFragment因此天然具备懒加载能力同时覆写showFirstLoading()把第一次可见的时机与全局 Loading 的展示衔接起来注释中特别提醒下拉刷新操作时不要用该全局 loading。这样可见时才请求数据与请求期间的统一 Loading/失败/空态 UI被有机整合在一个基类中子类只需实现initStatusLayout、initView、initListener和onLazyLoad四个方法即可完成一个带完整状态管理的懒加载页面。08. 总结懒加载方案的完整链路结合本文内容一个生产可用的 ViewPager Fragment 懒加载方案包含以下链路理解预加载的必然性setOffscreenPageLimit(0)会被源码强制修正为 1预加载无法通过 API 关闭只能在 Fragment 层面做可见才加载的优化明确可见性回调setUserVisibleHint()是FragmentPagerAdapter/FragmentStatePagerAdapter内部用于标识 primaryFragment 的回调是懒加载的基石双重标记确保时机正确isPreparedView 已构建防止空指针与isLazyLoaded防止重复加载配合getUserVisibleHint()并在onActivityCreated与setUserVisibleHint两处调用lazyLoad()解决首页首次展示无数据的问题生命周期复位在onDestroyView中重置isPrepared与isLazyLoaded保证 Fragment 视图重建后能再次触发懒加载占位与状态管理用ViewStub延迟加载真正的内容视图用StateLayoutManagerbuilder 模式 ViewStub 优化统一管理 Loading、空数据、异常、网络错误等全局状态二者都与懒加载时机天然衔接。这套方案完整继承自仓库文档 android/08.复杂控件/03.ViewPager懒加载.md其中的 PagerAdapter 协作细节、Fragment 适配器差异可参考 PagerAdapter详细介绍ViewStub 底层原理见 ViewStub源码分析真实业务落地案例可参考 仿抖音滑动分页视频其中对视频页可见性判断、setOffscreenPageLimit(1)的取舍有更深入的实践讨论。赞分享教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载相关推荐CloudReader性能优化骨架图加载与懒加载Fragment的实现指南CloudReader性能优化骨架图加载与懒加载Fragment的实现指南 CloudReader是一款基于网易云音乐UI设计的Android阅读应用通过R移动开发PHPExcel自动加载机制详解PSR-0规范与懒加载实现原理PHPExcel自动加载机制详解PSR 0规范与懒加载实现原理 PHPExcel是一个功能强大的PHP库用于读写Excel文件。对于新手和普通用户来说理解后端数据处理rsuite CascadeTree 异步数据加载实战基于 getChildren 的懒加载原理与完整示例rsuite CascadeTree 异步数据加载实战基于 getChildren 的懒加载原理与完整示例 导读 本文聚焦 rsuite 级联树组件 Casc前端UI组件上一篇Feapder框架中的BatchSpider分布式批次爬虫详解下一篇Fetch GitHub Hosts终极指南3分钟搞定GitHub访问加速创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询