目录
什么情况下会onmeasure会执行?
进入view
的measure
方法:
void measure(){ boolean forcelayout = (mprivateflags & pflag_force_layout) == pflag_force_layout; boolean specchanged = widthmeasurespec != moldwidthmeasurespec || heightmeasurespec != moldheightmeasurespec; boolean issepcexactly = measurespec.getmode(widthmeasurespec) == measurespec.exactly && measurespec.getmode(heightmeasurespec) == measurespec.exactly; boolean matchesspecsize = getmeasuredwidth() == measurespec.getsize(widthmeasurespec) && getmeasuredheight() == measurespec.getsize(heightmeasurespec); final boolean needslayout = specchanged && (salwaysremeasureexactly || !isspecexactly || !matchesspecsize); if(forcelayout || needlayout){ int cacheindex = forcelayout ? -1 : mmeasurecache.indexofkey(key); if (cacheindex < 0 || signoremeasurecache) { onmeasure(widthmeasurespec, heightmeasurespec); mprivateflags3 &= ~pflag3_measure_needed_before_layout; } else { long value = mmeasurecache.valueat(cacheindex); setmeasureddimensionraw((int) (value >> 32), (int) value); mprivateflags3 |= pflag3_measure_needed_before_layout; } } }
什么时候forcelayout=true
:
- 调用
requestlayout
- 调用
forcerequestlayout
什么时候needslayout=true
:
- 当长宽发生改变
什么时候调用了onmeasure>
方法:
forcelayouy=true
- 或者
mmeasurecache
没有当前的缓存
总结:
当调用了requestlayout
一定会测发重测过程.当forcelayout=false
的时候会去判断mmeasurecache
值.现在研究下这个mmeasurecache
class view{ longsparselongarray mmeasurecache; void measure(widthspec,heightspec){ --- long key = (long) widthmeasurespec << 32 | (long) heightmeasurespec & 0xffffffffl; int cacheindex = forcelayout ? -1 : mmeasurecache.indexofkey(key); if(cacheindex<0){ onmeasure(widthspec,heightspec); } moldwidthmeasurespec = widthmeasurespec; moldheightmeasurespec = heightmeasurespec; mmeasurecache.put(key,widhspec|heightspec); --- } }
这里可以看到oldwidthmeasurespec
和mmeasurecache
都是缓存上一次的值,那他们有什么不同呢?不同点就是,oldwidthmeasurespec>
不仅仅缓存了测量的spec
模式而且缓存了size
.但是mmeasurecache
只缓存了size
.从这行代码可以看出:
long key = (long) widthmeasurespec << 32 | (long) heightmeasurespec & 0xffffffffl;
这里一同运算就为了排除掉spec
造成的影响.
//不信你可以试下下面的代码 public class test { public static void main(string[] args) { long widthmeasurespec = makemeasurespec(10,0); long heightmeasurespec = makemeasurespec(20,0); long ss = widthmeasurespec << 32 | (long) heightmeasurespec & 0xffffffffl; system.out.println("==========" ss); } private static final int mode_mask = 0x3 << 30; public static int makemeasurespec(int size, int mode) { return (size & ~mode_mask) | (mode & mode_mask); } } //42949672980 //42949672980 //42949672980
什么时候mprivateflags
会被赋值pflag_force_layout
.
在view viewgrouup
的构造函数里面会主动赋值一次,然后在viewgroup.addview
时候会给当前view
的mprovateflags
赋值pflag_force_layout
.
为什么onmeasure会被执行两次?
void measure(int widthmeasurespec,int heightmeasurespec){ ---- boolean forcelayout = (mprivateflags & pflag_force_layout) == pflag_force_layout; if(forcelayout | needslayout){ onmeasure() } ---- } public void layout(int l, int t, int r, int b){ --- mprivateflags &= ~pflag_force_layout; --- }
在第一次触发到measure
方法时,forcelayoyt=true needslayout=true
,但是layout
方法还没触发到.
在第二次触发到measure>
方法时,forcelayout=true needslayout=false
,所以还是会进入onmeasure
方法.这次会执行layout
方法.然后我们在下次的时候forcelayout
就等于false
了.上面的这一段分析是分析的measure
内部如何防止多次调用onmeasure
.
分析外部是如何多次调用measure方法的
在activity
执行到onresume
生命周期的时候,会执行windowmanager.addview
操作,windowmanager
的具体实现类是windowmanagerimpl
然后addview
操作交给了代理类windowmanagerglobal
,然后在windowmanagerglobal
的addview
里面执行了viewrootimpl.setview
操作(viewrootimpl
对象也是在这个时候创建的),在viewrootimpl
会主动调用一次requestlayout
,也就开启了第一次的视图 测量 布局 绘制.
在setview
的时候主动调用了一次viewrootimpl.requestlayout
,注意这个requestlayout
是viewrootimpl
的内部方法,和view viewgroup
那些requestlayout
不一样.在viewrootimpl.requestlayout
内部调用了performtraversals
方法:
class viewrootimpl{ void performtraversals(){ if(layoutresuested){ //标记1 windowsizemaychanged |= measurehierarchy(host,lp,res,desiredwindowwidth,desiredwindowheight); } //标记2 performmeasure() performlayout() } void measurehierarchy(){ performmeasure() } }
从viewrootimpl
的执行逻辑你可以看出,在执行performlayout
之前,他自己就已经调用了两次performmeasure
方法.所以你现在就知道为啥了.
以上就是onmeasure被执行两次原理解析的详细内容,更多关于onmeasure被执行两次的资料请关注其它相关文章!