不缓存的viewpager
㈠ Android-ViewPager源码解析与性能优化
ViewPager高度设置为wrap_content或者具体的高度值无效,是因为ViewPager的onMeasure方法在度量宽高的时候,在方法体的最开始就直接调用了setMeasuredDimension()方法将自身的宽高度量,但是并没有在其onMeasure()计算完其具体的子View的宽高之后,重新度量一次自身的宽高
从这里我们可以看到,ViewPager的宽高会受其父容器的宽高的限制,但是并不会因为自身子View的宽高而影响ViewPager的宽高。
看setMeasuredDimension的源码调用可以看出,当父容器的高度确定时,ViewPager的宽高其实就是父容器的宽高,ViewPager就是在onMeasure方法一进来的时候就直接填充满整个父容器的剩余空间。在计算孩子节点之前,就已经计算好了ViewPager的宽高,在计算完孩子节点之后,并不会再去重新计算ViewPager的宽高。
自定义一个ViewPager,根据子View的宽高重新度量ViewPager的宽高。其实做法就是在自定义onMeasure的super.onMeasure(widthMeasureSpec, heightMeasureSpec);之前重新计算heightMeasureSpec,将原本ViewPager接收的父容器的限定的heightMeasureSpec替换成我们自定义的heightMeasureSpec。
但是这样的做法,会有种问题,即在ViewPager的子View是采用LinearLayout作为根布局的时候,并且给LinearLayout设置了固定的高度值,那么会出现ViewPager动态高度无效的问题
其实具体的做法,就是仿造measureChild的做法,自定义子View的heightMeasureSpec然后度量整个子View,其实子View的宽度也可以这样做。
这里其实是源码层做了限制,在setOffscreenPageLimit中设置了一个默认值,而这个默认值的大小为1
所以从这里可以看出,ViewPager的最小缓存的limit是1,而不能小于1,当小于1的时候就会被强制的设置为1。
而populate()函数就是用来处理ViewPager的缓存的。
populate()的生命周期是与Adapter的生命周期绑定的。
其实在setOffscreenPageLimit()的时候,调用的populate(),而populate()内部调用的
而pupulate(int newCurrentItem)方法在另一处调用的地方就是在setCurrentItem。
其实ViewPager缓存都是基于ItemInfo这个类来进行的,
看下ViewPager.addNewItem的源码
其实ViewPager.addNewItem就是通过调用Adapter.instantiateItem来创建对应的View,并且将View保存到ItemInfo中的object属性,并且判断ViewPager缓存中是否已经有ItemInfo,如果没有,则添加,如果有则做修改替换
从分析FragmentStatePagerAdapter来看,setUserVisibleHint方法会优先于Fragment的生命周期函数 执行。因为在FragmentStatePagerAdapter中提交事务,是在调用finishUpdate方法中进行的,只有提交事务的时候,才会去执行Fragment的生命周期。
FragmentStatePagerAdapter中的instantiateItem和destroyItem都实现了对fragment的事务的添加和删除,而finishUpdate实现了事务的提交,所以在实现FragmentStatePagerAdapter的时候,并不需要重写instantiateItem和destroyItem
㈡ ViewPager详解
如果你不使用setoffscreenpagelimit(int limit)这个方法去设置默认加载数的话是会默认加载页面的左右两页的,也就是说当你进入viewpager第一页的时候第二页和第一页是会被一起加载的,这样同时加载就会造成一些问题,试想我们如果设置了setoffscreenpagelimit为3的话,那么进入viewpager以后就会同时加载4个fragment,像我们平时的项目中在这些fragment中一般都是会发送网络请求的,也就是说我们有4个fragment同时发送网络请求去获取数据,这样的结果显而易见给用户的体验是不好的(如:浪费用户流量,造成卡顿等等)。
ViewPager的预加载机制。默认缓存当前页面的前后各一个。
参考: ViewPager懒加载
PagerAdapter有两个子类:
FragmentPagerAdapter和FragmentPagerStateAdapter
参考: PagerAdapter深度解析和实践优化
ViewPager2实现是RecyclerView
ViewPager2的Adapter是FragmentStateAdapter
㈢ android studio viewpager怎么让其不缓存
设置单实例。
假如你一个Fragment名字是TestFragment,你可以这样
TestFragment mTestFragment;
public TestFragment getInstance(){
if( TestFragment == null)
mTestFragment = new TestFragment();
return mTestFragment;
}
㈣ ViewPage在运行的时候,各个fragment的执行过程是什么样的
setUserVisibleHint就是告诉系统,fragment可见的时候才加载数据
如果fragment不多的话, 可以在activty里面直接初始化默认内容,等到翻个某个fragment的时候再加载网络或者本地内容。
ViewPager默认会缓存三页,即当前页和左右两页。
滑动到第三页时,第一页已经不缓存了,就调用PagerAdapter里的destroyItem方法销毁了,再回来时调用了PagerAdapter的instantiateItem重新创建了一个。FragmentPagerAdapter的缓存策略更复杂一些,参见[FragmentPagerAdapter与FragmentStatePagerAdapter区别]
㈤ 求大神解决!!android viewpager缓存,fragment动画
取消viewpager预加载,重写一个基类fragment,判断该fragment是否显示,没显示就不加载界面。你网络,我手机端写代码费时,网络viewpager取消预加载,一大堆,都挺好懂得没有多少代码。
㈥ 谈谈RecyclerView中的缓存
Android深入理解RecyclerView的缓存机制
RecyclerView在项目中的使用已经很普遍了,可以说是项目中最高频使用的一个控件了。除了布局灵活性、丰富的动画,RecyclerView还有优秀的缓存机制,本文尝试通过源码深入了解一下RecyclerView中的缓存机制。
RecyclerView做性能优化要说复杂也复杂,比如说布局优化,缓存,预加载等等。其优化的点很多,在这些看似独立的点之间,其实存在一个枢纽:Adapter。因为所有的ViewHolder的创建和内容的绑定都需要经过Adaper的两个函数onCreateViewHolder和onBindViewHolder。
因此我们性能优化的本质就是要减少这两个函数的调用时间和调用的次数。如果我们想对RecyclerView做性能优化,必须清楚的了解到我们的每一步操作背后,onCreateViewHolder和onBindViewHolder调用了多少次。因此,了解RecyclerView的缓存机制是RecyclerView性能优化的基础。
为了理解缓存的应用场景,本文首先会简单介绍一下RecyclerView的绘制原理,然后再分析其缓存实现原理。
RecyclerView滑动时会触发onTouchEvent#onMove,回收及复用ViewHolder在这里就会开始。我们知道设置RecyclerView时需要设置LayoutManager,LayoutManager负责RecyclerView的布局,包含对ItemView的获取与复用。以LinearLayoutManager为例,当RecyclerView重新布局时会依次执行下面几个方法:
上述的整个调用链:onLayoutChildren()->fill()->layoutChunk()->next()->getViewForPosition(),getViewForPosition()即是是从RecyclerView的回收机制实现类Recycler中获取合适的View,下面主要就来从看这个Recycler#getViewForPosition()的实现。
上述逻辑用流程图表示:
RecyclerView在Recyler里面实现ViewHolder的缓存,Recycler里面的实现缓存的主要包含以下5个对象:
public final class Recycler {
final ArrayList mAttachedScrap = new ArrayList<>();
ArrayList mChangedScrap = null;
RecyclerView在设计的时候讲上述5个缓存对象分为了3级。每次创建ViewHolder的时候,会按照优先级依次查询缓存创建ViewHolder。每次讲ViewHolder缓存到Recycler缓存的时候,也会按照优先级依次缓存进去。三级缓存分别是:
使用自定义ViewCacheExtension后,view离屏后再回来不会走onBindViewHolder()方法。
holder.setIsRecyclable(false),这样的话每次都会走onCreateViewHolder()和onBindViewHolder()方法
1.提前初始化viewHolder,放到缓存池中
viewPool.putRecycledView(adapter.onCreateViewHolder(recyclerView, 1))
2.提前初始化view,在onCreateViewHolder的时候去取view
3.自定义ViewCacheExtension
4.适当的增加cacheSize
4.公用缓存池,比如多个viewPager+fragment场景使用,或者全局单利缓存池,感觉用户不大。
有2中做法有值
第一种
第二种
不会,因为prefetch(GapWorker中的一个方法)之后mViewCacheMax会变成mRequestedCacheMax + extraCache
有2种方式可以让缓存失效
第一种
recyclerView.setItemViewCacheSize(-1)
第二种
recyclerView.setItemViewCacheSize(0)
layoutManager.isItemPrefetchEnabled = false
设置不缓存后,来回滑动让view进入屏幕离开屏幕,viewHolder的item时会多次走onBindViewHolder()方法。
㈦ viewpager的缓存加载机制
viewpager有缓存预加载机制,主要使用setOffscreenPageLimit(int limit)
1.setOffscreenPageLimit(int limit) 解释
缓存:左右都会缓存limit个页面,比如limit缓存数量是2,在tab3,那会缓存tab1、tab2、tab4、tab5,如果其他界面已经缓存过的话会调用onDestroyView销毁
预加载:limit缓存数量是2,如果在tab1会预加载tab2、tab3,切换到tab2的话会预加载tab4,
2.viewpager源码分析
viewpager继承viewgroup当然也要走onmeasure,ondraw,onlayout方法,主要看onmeasure里面的populate()方法
这里的mAdapter是PagerAdapter
fragmentpageradapter就是你写的适配器,接下来看具体实现
所以我们可以用uservisiblehint来懒加载,需要注意的是uservisiblehint不是生命周期函数,初始化的时候他的执行在fragment的onattch之前
Fragment 生命周期按先后顺序:onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume ->onPause -> onStop -> onDestroyView -> onDestroy -> onDetach
㈧ 深入解析ViewPager懒加载
ViewPager默认情况的加载,会默认预加载一个的布局到ViewPager中,这就时ViewPager的预加载。setOffscreenPageLimit可以通过这个方法去设置预加载布局的个数。
从上面可以看出,预加载的页面个数大于等于1。
在缓存数量更新的时候,要执行缓存方法polulate()。去看看里面做了啥事
前面提到了创建ViewPager.ItemInfo,这个是什么
注释①:调用instantiateItem来创建object,在FragmentPagerAdapter的instantiateItem这个方法中,创建的就是fragment。所以缓存的就是fragment,fragment在创建时,会有ui操作,网络操作,在还未可见的时候,就去初始化fragment非常消耗性能,所以懒加载的方式来加载,不去缓存fragment。
㈨ ViewPager的缓存页面 与 预加载
1、在setUserVisibleHint(boolean isVisibleToUser),判断参数isVisibleToUser为true时才去加载数据,false则停止加载
2、解决崩溃:由于setUserVisibleHint(boolean isVisibleToUser)在onCreateView()之前执行,有可能view还未被创建。所以需要设置一个tag,在onCreateView()中置为true。
然后在tag为true时才去加载/停止加载数据
3、解决第一次进入不加载:第一次进入,setUserVisibleHint(boolean isVisibleToUser)在onCreateView()之前执行,加载数据的逻辑并未执行,
所以需要我们在onCreateView()中手动执行加载逻辑,在getUserVisibleHint()为true的时候执行。
Fragment 生命周期按先后顺序:
onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume ->
onPause -> onStop -> onDestroyView -> onDestroy -> onDetach
ViewPager + Fragment 的实现我们需要关注的几个生命周期有: onCreatedView + onResume + onPause + onDestroyView非生命周期函数:setUserVisibleHint + onHiddenChanged
㈩ ViewPager系列文章(一)- ViewPager源码分析及加载页面原理图
1>:点击 viewPager.setAdapter进入下边源码,会调用 populate() 方法,这个方法作用是创建和销毁子条目(子item):
在populate()方法中:
创建ItemView:mAdapter.instantiateItem(this, position);
销毁ItemView:mAdapter.destroyItem(this, pos, ii.object);
所以由ViewPager的源码可以看出,ViewPager里边无论放多少个页面都不会内存溢出,它会不断的去创建和销毁view;
和 ListView、RecyclerView不一样,ListView、RecyclerView是会不断的复用view,而viewpager是不断的创建和销毁view
轮播图刚打开默认显示当前页,是第一页,默认会缓存左右两个页面,如果左边没有,只有右边有,那么右边是第0页,当前页是第一页;
如果你滑动到第1页,ViewPager会默认把 左边第0页 和 右边第2页 创建出来;
如果你滑动到第2页,ViewPager会默认把第1页和第3页创建出来,而原来的第0页就会变成需要销毁的页面;
如果想要缓存多页,可以调用setOffscreenPageLimit()方法:
setOffscreenPageLimit(1):ViewPager机制默认就是缓存1,表示左边、右边各缓存1页,加上自己,总共是3页,其余页面全部销毁;
setOffscreenPageLimit(2):表示默认给左右各缓存2页,共4页,加上自己,总共缓存5页,其余页面全部销毁;
setOffscreenPageLimit(3):表示默认给左右各缓存3页,共6页,加上自己,总共缓存7页,其余页面全部销毁;
因为 smoothScrollTo()滑动方法也调用populate(),而populate()方法维护了当前显示页面和 左右缓存的页面,就能做到无限滑动而不出问题;
A:从populate()源码中可知:先判断页面是否在缓存范围内:如果在,则addNewItem添加进来,否则在destroyItem掉;
B:ViewPager会缓存左右两边页面+1(当前显示页面),默认认为当前页面的 左右两边各有1个,用户可以手动调用setOffscreenPageLimit()方法设置数量,如果传的值小于1,就默认设置为1;
ViewPager实际示意图如下: