最近ViewPager2发布了1.0.0-alpha04版本,新增offscreenPageLimit功能,该功能在ViewPager上并不友好,如下是官方对于ViewPager2的介绍:
众所周知,ViewPager有两个毛病:不能关闭预加载和更新Adapter不生效
,因此开头我为何说offscreenPageLimit在ViewPager上十分不友好;本质上是由于offscreenPageLimit不能设置成0(设置成0就是想象中的关闭预加载)。
上面是ViewPager默认状况下的加载示意图,当切换到当前页面时,会默认预加载左右两侧的布局到ViewPager中,尽管两侧的View并不可见的,咱们称这种状况叫预加载;因为ViewPager对offscreenPageLimit设置了限制,页面的预加载是不可避免。ViewPager的源码以下:android
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实现懒加载的方法,经常使用的有如下几个方法:数组
先说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库,引入时只须要在build.gradle添加以下依赖便可:网络
implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha04'
而后,在布局中引入:app
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
ViewPager2能够和Adapter一块儿使用,也能够和Fragment一块儿使用。ide
ViewPager2 viewPager = findViewById(R.id.view_pager2); viewPager.setAdapter(new RecyclerView.Adapter<ViewHolder>() { @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:布局
在1.0.0-alpha04版本中,ViewPager2提供了离屏加载功能,该功能和ViewPager的预加载存的的意义彷佛是同样的。fetch
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的区别,咱们看一下布局方面的对比:gradle
为了对比二者加载布局的效果,我准备了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上;
目前,ViewPager2对Fragment的支持只能使用FragmentStateAdapter来实现,使用起来也是很是简单,例如:
默认状况下,ViewPager2是开启预加载关闭离屏加载的,这种状况下,切换页面对Fragment生命周期有何影响呢?如下是来自网络上的两个图:
问题一:关闭预加载对Fragment的影响: 通过验证,是否开启预加载,对Fragment的生命周期没有影响,结果和默认上图是同样的;
问题二:开启离屏加载对Fragment的影响: 设置offscreenPageLimit=1时:
从打印日志能够看出,offscreenPageLimit=1时,ViewPager2在第1页会加载两条数据,而且会把下一页View提早加载进来;之后每滑一页,会加载下一页数组,直到第5页,会移除第1页的Fragment;第6页会移除第2页的Fragment。
针对这次的更新,ViewPager2主要有如下一些特性:
经过此次ViewPager2更新,官方貌似要发力替换ViewPager了,不管是它高效的复用仍是自带懒加载,亦或是更新有效的Adapter,都要比ViewPager更增强大。