深入解析 Android 中 View 的工作原理(下)
我们继续上一篇陈述,这是《深入解析 Android 中 View 的工作原理》的最后一篇,主要陈述layout的过程和draw的过程。
layout的过程
ViewGroup的位置确定后,它在onLayout中会遍历所有的子元素并调用子元素layout方法,子元素layout方法中又会调用onLayout方法,View的layout方法确定自身的位置,而onLayout方法方法确定子孩子的位置:
layout方法的大致流程如下:首先会通过setFrame方法来确定mLeft;mTop;mBottom;mRight;只要这四个点一旦确定,那么View在父容器中的位置就确定了,接着会调用onLayout方法,该方法目的是父容器来确定子元素的位置,无论是View还是ViewGroup都没有实现onLayout方法,我们查看LinearLayout的onLayout方法:
查看layoutVertical中关键代码:
这个方法会遍历所有的子元素并调用setChildFrame方法来为子元素指定对应的位置,其中childTop的数值会不断的增大,这意味着后面的子元素还位于靠下的位置,刚好符合竖直的LinearLayout的特性,setChildFrame方法中不过是调用了子元素的Layout方法而已。
同时,会发现setChildFrame中的width和height实际上就是子元素的测量宽高
View的layout方法中会通过setFrame方法去设置子元素四个顶点的位置,这样子元素的位置就可以确定:
接下来是View的getWidth和getHeight方法,结合里面的实现,可以发现他们分别返回的就是View测量的宽度和高度
layout总结
1、layout也是从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
2、View.layout方法可被重载,ViewGroup.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑。
3、measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的。
4、凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的。
5、使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值。
draw的过程
View的绘制过程遵循以下几步:
1、绘制背景background.draw(canvas)
2、绘制自己(onDraw)
3、绘制 children(dispatchDraw)
4、绘制装饰(onDrawScrollBars)
View的绘制过程的传递是通过dispatchDraw实现的,dispatchdraw会遍历调用所有子元素的draw方法。如此draw事件就一层一层的传递下去。
draw总结
1、如果该View是一个ViewGroup,则需要递归绘制其所包含的所有子View。
2、View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。
3、View的绘制是借助onDraw方法传入的Canvas类来进行的。
4、在获取画布剪切区(每个View的draw中传入的Canvas)时会自动处理掉padding,子View获取Canvas不用关注这些逻辑,只用关心如何绘制即可。
5、默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。
好了,《深入解析 Android 中 View 的工作原理》写到这里就完结了,大家有没有对自定义View有一个深入的了解呢。
最后附上小编整理出来的Android相关的学习思维导图,让大家有个学习的方向。
Android进阶
Android前沿技术
Flutter
移动架构师
需要这些安卓学习资料和面试资料的大伙需要的关注+点赞+加群:185873940 免费获取!
群内还有许多免费的关于高阶安卓学习资料,包括高级UI、性能优化、架构师课程、 NDK、混合式开发:ReactNative+Weex等多个Android技术知识的架构视频资料,还有职业生涯规划及面试指导。