前言

最近ViewPager2发布了1.0.0-alpha04版本,新增offscreenPageLimit功能,该功能在ViewPager上并不友好,以下是官方对于ViewPager2的介绍:

众所周知,ViewPager有两个毛病:不能关闭预加载和更新Adapter不生效,所以开头我为什么说offscreenPageLimit在ViewPager上十分不友好;本质上是因为offscreenPageLimit不能设置成0(设置成0就是想象中的关闭预加载)。

上面是ViewPager默认情况下的加载示意图,当切换到当前页面时,会默认预加载左右两侧的布局到ViewPager中,尽管两侧的View并不可见的,我们称这种情况叫预加载;由于ViewPager对offscreenPageLimit设置了限制,页面的预加载是不可避免。ViewPager的源码如下:

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

public void setOffscreenPageLimit(int limit) {

if (limit < DEFAULT_OFFSCREEN_PAGES) {//不允许小于1

Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "

+ DEFAULT_OFFSCREEN_PAGES);

limit = DEFAULT_OFFSCREEN_PAGES;

}

if (limit != mOffscreenPageLimit) {

mOffscreenPageLimit = limit;

populate();

}

}

不过,ViewPager强制预加载的逻辑在Fragment配合ViewPager使用时依然存在。为了不进行预加载,可以使用Fragment懒加载。先说一下ViewPager配合PagerAdapter实现懒加载的方法,常用的有以下几个方法:

instantiateItem(ViewGroup container, int position):初始化ItemView,返回需要添加ItemView;

destroyItem(iewGroup container, int position, Object object):销毁ItemView,移除指定的ItemView;

isViewFromObject(View view, Object object):View和Object是否对应;

setPrimaryItem(ViewGroup container, int position, Object object) :当前页面的主Item;

getCount():获取Item个数

先说setPrimaryItem(ViewGroup container, int position, Object object),该方法表示当前页面正在显示主要Item,何为主要Item?如果预加载的ItemView已经划入屏幕,当前的PrimaryItem依然不会改变,除非新的ItemView完全划入屏幕,且滑动已经停止才会判断,涉及代码如下:

由于ViewPager不可避免的进行布局预加载,造成PagerAdapter必须提前调用instantiateItem(ViewGroup container, int position)方法,instantiateItem()是创建ItemView的唯一入口方法,所以PagerAdapter的实现类FragmentPagerAdapter和FragmentStatePagerAdapter必须抓住该方法进行Fragment对象的创建。例如:

需要说明的是,FragmentPagerAdapter和FragmentStatePagerAdapter一股脑的在instantiateItem()中进行创建且进行add或attach操作,并没有在setPrimaryItem()方法中对Fragment进行操作。因此,预加载会导致不可见的Fragment一股脑的调用onCreate、onCreateView、onResume等方法,用户只能通过Fragment.setUserVisibleHint()方法进行识别。

大多数的懒加载都是对Fragment做手脚,结合生命周期方法和setUserVisibleHint状态,控制数据延迟加载,而布局只能提前进入;

ViewPager2

使用ViewPager2之前,需要先引入ViewPager2库,引入时只需要在build.gradle添加如下依赖即可:

implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha04'

然后,在布局中引入:

android:id="@+id/view_pager"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1" />

ViewPager2可以和Adapter一起使用,也可以和Fragment一起使用。

ViewPager2 viewPager = findViewById(R.id.view_pager2);

viewPager.setAdapter(new RecyclerView.Adapter() {

@Override

public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_card_layout, parent, false);

ViewHolder viewHolder = new ViewHolder(itemView);

return viewHolder;

}

@Override

public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

holder.labelCenter.setText(String.valueOf(position));

}

@Override

public int getItemCount() {

return SIZE;

}

}));

static class ViewHolder extends RecyclerView.ViewHolder{

private final TextView labelCenter;

public ViewHolder(@NonNull View itemView) {

super(itemView);

labelCenter = itemView.findViewById(R.id.label_center);

}

}

当然,ViewPager2也可以和Fragment配合使用。

viewPager.setAdapter(new FragmentStateAdapter(this) {

@NonNull

@Override

public Fragment getItem(int position) {

return new VSFragment();

}

@Override

public int getItemCount() {

return SIZE;

}

});

ViewPager2和ViewPager在使用方式上差不多,主要有以下一些API:

setAdapter() 设置适配器

setOrientation() 设置布局方向

setCurrentItem() 设置当前Item下标

beginFakeDrag() 开始模拟拖拽

fakeDragBy() 模拟拖拽中

endFakeDrag() 模拟拖拽结束

setUserInputEnabled() 设置是否允许用户输入/触摸

setOffscreenPageLimit()设置屏幕外加载页面数量

registerOnPageChangeCallback() 注册页面改变回调

setPageTransformer() 设置页面滑动时的变换效果

离屏加载

在1.0.0-alpha04版本中,ViewPager2提供了离屏加载功能,该功能和ViewPager的预加载存的的意义似乎是一样的。

public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = 0;

public void setOffscreenPageLimit(int limit) {

if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) {

throw new IllegalArgumentException(

"Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0");

}

mOffscreenPageLimit = limit;

// Trigger layout so prefetch happens through getExtraLayoutSize()

mRecyclerView.requestLayout();

}

为了方便分析ViewPager2和ViewPager的区别,我们看一下布局方面的对比:

布局对比

为了对比两者加载布局的效果,我准备了LinearLayout同时展示ViewPager和ViewPager2,设置相同的Item布局和数据源,然后用Android布局分析工具抓取两者的布局结构,效果如下:

从分析结果来看,ViewPager会默认会预布局两侧各一个布局,ViewPager2默认不进行预布局,主要由各自的默认offscreenPageLimit参数决定,ViewPager默认为1且不允许小于1,ViewPager2默认为0。

分析运行结果,在设置相同的offscreenPageLimit时,两者都会预布局左右(上下)两者的offscreenPageLimit个ItemView;

从对比结果上来看,ViewPager2的offscreenPageLimit和ViewPager运行结果一样,但是ViewPager2最小offscreenPageLimit可以设置为0;

预加载和缓存

ViewPager2预加载即RecyclerView的预加载,代码在RecyclerView的GapWorker中。ViewPager2会默认开启预加载,表现形式是在拖动控件或者Fling时,可能会预加载一条数据;下面是预加载的示意图:

如果要关闭预加载,可以使用下面的代码:

((RecyclerView)viewPager.getChildAt(0)).getLayoutManager().setItemPrefetchEnabled(false);

预加载的开关在LayoutManager上,只需要获取LayoutManager并调用setItemPrefetchEnabled()即可控制开关;

ViewPager2默认会缓存2条ItemView,而且在最新的RecyclerView中可以自定义缓存Item的个数,方法如下:

public void setItemViewCacheSize(int size) {

mRecycler.setViewCacheSize(size);

}

预加载和缓存在View层面没有本质的区别,都是已经准备了布局,但是没有加载到parent视图上; 预加载和离屏加载在View层面有本质的区别,离屏加载的View已经添加到parent上;

预加载对Fragment的支持

目前,ViewPager2对Fragment的支持只能使用FragmentStateAdapter来实现,使用起来也是非常简单,例如:

默认情况下,ViewPager2是开启预加载关闭离屏加载的,这种情况下,切换页面对Fragment生命周期有何影响呢?以下是来自网络上的两个图:

问题一:关闭预加载对Fragment的影响: 经过验证,是否开启预加载,对Fragment的生命周期没有影响,结果和默认上图是一样的;

问题二:开启离屏加载对Fragment的影响: 设置offscreenPageLimit=1时:

从打印日志可以看出,offscreenPageLimit=1时,ViewPager2在第1页会加载两条数据,并且会把下一页View提前加载进来;以后每滑一页,会加载下一页数组,直到第5页,会移除第1页的Fragment;第6页会移除第2页的Fragment。

小结

针对此次的更新,ViewPager2主要有以下一些特性:

目前ViewPager2对Fragment支持只能用FragmentStateAdapter,FragmentStateAdapter在遇到预加载时,只会创建Fragment对象,不会把Fragment真正的加入到布局中,所以自带懒加载效果;

FragmentStateAdapter不会一直保留Fragment实例,回收的ItemView也会移除Fragment,所以得做好Fragment重建后恢复数据的准备;

FragmentStateAdapter在遇到offscreenPageLimit>0时,处理离屏Fragment和可见Fragment没有什么区别,所以无法通过setUserVisibleHint判断显示与否;

通过这次ViewPager2更新,官方貌似要发力替换ViewPager了,无论是它高效的复用还是自带懒加载,亦或是更新有效的Adapter,都要比ViewPager更加强大。

android viewpager 缓存,ViewPager2重大更新,支持offscreenPageLimit相关推荐

  1. oppo三年Android更新,OPPO Find X3系列将提供3年系统更新支持

    原标题:OPPO Find X3系列将提供3年系统更新支持 据最新的消息显示,OPPO Find X3系列新机将会有着3年的系统更新支持,这意味着OPPO至少会提供至少2个Android大版本的系统更 ...

  2. Android面试总结(持续更新修改)

    ###Android面试总结(持续更新修改) 1.Android 的四大组件是哪些,它们的作用? ①Activity是Android程序与用户交互的窗口,是Android构造块中最基本的一种,它需要为 ...

  3. Android开源项目汇总20150712更新

    Trinea (github)总结的Android开源项目汇总 转此记录研究 附一些内容 目前包括: Android开源项目第一篇--个性化控件(View)篇 包括ListView.ActionBar ...

  4. Android面试题集锦(持续更新)

    一.java 熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,甚至要你知道有哪些不足,怎么改进,还有一些java有关的一些算法,设计模式等等. (一)ja ...

  5. Android开源项目集合(不断更新)

    Android开源项目集合(不断更新) 女神节快乐~~~ 休息片刻23 PlayAndroid advanced-java FFmpegAndroidCameraEncoder ScaleRuler ...

  6. Android 学习记录(持续更新)

    Android 学习记录(持续更新) 1.AndroidManifest.xml 详解: http://www.jb51.net/article/73731.htm (AndroidManifest. ...

  7. android视频缓存框架 [AndroidVideoCache](https://github.com/danikula/AndroidVideoCache) 源码解析与评估

    文章目录 android视频缓存框架 [AndroidVideoCache](https://github.com/danikula/AndroidVideoCache) 源码解析与评估 引言 使用方 ...

  8. Android面试宝典2022-(停止更新,请看面试专栏)

    Android面试宝典2020-持续更新 一.Java基础 1.java基本数据类型和引用类型 2.object equals和==的区别 equals和hashcode的关系? 3.static关键 ...

  9. 高并发场景下,到底先更新缓存还是先更新数据库?

    在大型系统中,为了减少数据库压力通常会引入缓存机制,一旦引入缓存又很容易造成缓存和数据库数据不一致,导致用户看到的是旧数据. 为了减少数据不一致的情况,更新缓存和数据库的机制显得尤为重要,接下来带领大 ...

最新文章

  1. 完整的Python 3和树莓Pi大师课 Complete Python 3 and Raspberry Pi Masterclass
  2. pandas中使用rolling.corr函数计算两个时间序列数据列之间的滚动相关性(Rolling correlations)、例如,计算两种商品销售额之间的3个月的滚动相关性
  3. CSS3的弹性盒子flex详解(2)
  4. inspect python_python之inspect模块
  5. android中shape资源定义,Android中drawable使用Shape资源
  6. (35)FPGA面试技能提升篇(AD、DA、时钟芯片)
  7. PKMS的queryIntentActivities分析
  8. windows下重设mysql的root密码
  9. c语言程序 题库管理,C语言程序设计题库管理.doc
  10. Oracle DG Broker 进行 SwitchOver Failover,Failover后恢复主从同步
  11. CMD使用教程-整理最全面的cmd用法
  12. kali破解wifi 密码(仅供学习)
  13. 移动硬盘插入提示需要格式化RAW_分区变成RAW格式怎么办?
  14. 关于JavaScript打印去掉页眉页脚
  15. BIM+GIS技术为工程数字化转型提供了新的契机
  16. vc mfc Edit SetFocus 设置焦点 无效 失败
  17. Linux报错:Syntax error: “(“ unexpected解决办法,elf
  18. 2022年终总结——脚踏实地,勇敢做自己
  19. 复盘在项目管理中的应用
  20. matlab试差法,研究生-化工单元与过程的稳态模拟方法(1).ppt

热门文章

  1. 解析DeepLabv3+的网络结构及代码【Pytorch版】
  2. Python利用openpyxl来操作Excel
  3. 工业锅炉模拟实训装置
  4. linux下进程监控6,系统运维|Linux系统监控神器-Collectl
  5. 石化盈科与竹云携手打造石化集团零信任标杆案例
  6. 【转】Qt 多线程串口通信问题?
  7. 单张地图游戏服务器承载_角度单张地图的简单指南
  8. 安卓dialog只显示半透明背景
  9. w3m 设置用户名密码自动登录
  10. JavaScript唐山市地图网页代码