android requestLayout、invalidate与postInvalidate

原文:https://blog.csdn.net/a553181867/article/details/51583060

requestLayout

1.当我们动态移动一个View的位置,或者View的大小、形状发生了变化的时候,我们可以在view中调用这个方法,即:

view.requestLayout();

2.在requestLayout方法中,首先先判断当前View树是否正在布局流程,接着为当前子View设置标记位,该标记位的作用就是标记了当前的View是需要进行重新布局的,接着调用mParent.requestLayout方法,这个十分重要,因为这里是向父容器请求布局,即调用父容器的requestLayout方法,为父容器添加PFLAG_FORCE_LAYOUT标记位,而父容器又会调用它的父容器的requestLayout方法,即requestLayout事件层层向上传递,直到DecorView,即根View,而根View又会传递给ViewRootImpl,也即是说子View的requestLayout事件,最终会被ViewRootImpl接收并得到处理。纵观这个向上传递的流程,其实是采用了责任链模式,即不断向上传递该事件,直到找到能处理该事件的上级,在这里,只有ViewRootImpl能够处理requestLayout事件。
 

3.requestLayout最终调用scheduleTraversals方法,这个方法是一个异步方法,最终会调用到ViewRootImpl#performTraversals方法,这也是View工作流程的核心方法,在这个方法内部,分别调用measure、layout、draw方法来进行View的三大工作流程

首先是判断一下标记位,如果当前View的标记位为PFLAG_FORCE_LAYOUT,那么就会进行测量流程,调用onMeasure,对该View进行测量,接着最后为标记位设置为PFLAG_LAYOUT_REQUIRED,这个标记位的作用就是在View的layout流程中,如果当前View设置了该标记位,则会进行布局流程,对于draw()方法走不走取决于left top right bottom有没有发生变化

4.子View调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会调用三大流程,从measure开始,对于每一个含有标记位的view及其子View都会进行测量、布局、绘制。

view和ViewGrope的requestLayout方法都是View中实现的

自己的体会:分三步第一步:就是给调用requestLayout的View和递归他的父View直到decorView这些View加PFLAG_FORCE_LAYOUT这个标记。第二步:ViewRootImpl调用RequestLayout(主要是执行performTraversals),从根View:DecorView往下找有PFLAG_FORCE_LAYOUT标记的View,进行测量流程调用onMeasure,然后在给这个View标记位设置成PFLAG_LAYOUT_REQUIRED。第三步:继续判断标记位是否为PFLAG_LAYOUT_REQUIRED,如果是则继续执行布局流程调用layout

 

invalidate

该方法的调用会引起View树的重绘,常用于内部调用(比如 setVisiblity())或者需要刷新界面的时候,需要在主线程(即UI线程)中调用该方法。

1.invalidate有多个重载方法,但最终都会调用invalidateInternal方法,在这个方法内部,进行了一系列的判断,判断View是否需要重绘,接着为该View设置标记位,然后把需要重绘的区域传递给父容器,即调用父容器的invalidateChild方法。 

invalidate有多个重载方法,如:invalidate() 其实是调用的invalidate(boolean invalidateCache),invalidate(boolean invalidateCache)这两个最终会执行invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);可以看出这个是把整个view重绘,还有一类重载方法如:invalidate(int l, int t, int r, int b)最终调用invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);、invalidate(Rect dirty)最终调用invalidateInternal(dirty.left - scrollX, dirty.top - scrollY, dirty.right - scrollX, dirty.bottom - scrollY, true, false);这一类是把规定区域重绘

2.父容器的invalidateChild方法即ViewGroup#invalidateChild:在该方法内部,先设置当前视图的标记位,接着有一个do…while…循环,该循环的作用主要是不断向上回溯父容器,求得父容器和子View需要重绘的区域的并集(dirty)。当父容器不是ViewRootImpl的时候,调用的是ViewGroup的invalidateChildInParent方法是:ViewGroup#invalidateChildInParent:这个方法做的工作主要有:调用offset方法,把当前dirty区域的坐标转化为父容器中的坐标,接着调用union方法,把子dirty区域与父容器的区域求并集,换句话说,dirty区域变成父容器区域。最后返回当前视图的父容器,以便进行下一次循环,由于不断向上调用父容器的方法,到最后会调用到ViewRootImpl的invalidateChildInParent方法:ViewRootImpl#invalidateChildInParent:该方法所做的工作与上面的差不多,都进行了offset和union对坐标的调整,然后把dirty区域的信息保存在mDirty中,最后调用了scheduleTraversals方法,触发View的工作流程,由于没有添加measure和layout的标记位,因此measure、layout流程不会执行,而是直接从draw流程开始。

3.总结一下invalidate方法,当子View调用了invalidate方法后,会为该View添加一个标记位,同时不断向父容器请求刷新,父容器通过计算得出自身需要重绘的区域,直到传递到ViewRootImpl中,最终触发performTraversals方法,进行开始View树重绘流程(只绘制需要重绘的视图)。

自己的体会:分两步第一步:给调用invalidate的View标记PFLAG_DIRTY并把要重绘的区域保存,然后递归调用父View的invalidateChild方法,如果不到最上层则调用ViewGroup#invalidateChildInParent:把父View的标记也制成PFLAG_DIRTY同时把子View需要重绘区域和父View需要重绘区域取并集保存起来然后递归直到ViewRootImpl的invalidateChildInParent该方法也是求需要重绘区域并集然后保存最后调用scheduleTraversals方法,触发View的工作流程,由于没有添加measure和layout的标记位,因此measure、layout流程不会执行,而是直接从draw流程开始,最终调用drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets)其中有canvas = mSurface.lockCanvas(dirty);然后mView.draw(canvas)

 

 

postInvalidate

这个方法与invalidate方法的作用是一样的,都是使View树重绘,但两者的使用条件不同,postInvalidate是在非UI线程中调用,invalidate则是在UI线程中调用。 

View#postInvalidate:只有attachInfo不为null的时候才会继续执行,即只有确保视图被添加到窗口的时候才会通知view树重绘,因为这是一个异步方法,如果在视图还未被添加到窗口就通知重绘的话会出现错误,所以这样要做一下判断。接着调用了ViewRootImpl#dispatchInvalidateDelayed方法:这里用了Handler,发送了一个异步消息到主线程,显然这里发送的是MSG_INVALIDATE,即通知主线程刷新视图,具体的实现逻辑我们可以看看该mHandler的实现:可以看出,参数message传递过来的正是View视图的实例,然后直接调用了invalidate方法,然后继续invalidate流程。

 

requestLayout、invalidate的区别:

requestlayout and invalidate.jpg

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值