1. View的绘制流程:从onMeasure到onDraw的完整拆解
很多朋友在面试WMS相关岗位时,一听到“View绘制流程”就头疼,感觉概念又多又绕。其实,你完全可以把一个View的绘制过程想象成装修一间毛坯房。onMeasure就是量房,onLayout就是规划家具摆放位置,onDraw就是最后的粉刷和装饰。今天我就用这个装修的比喻,带你把这个流程彻底搞明白,顺便聊聊面试官最爱问的细节。
onMeasure到底是干什么的? 它的核心职责就是“测量”。想象一下,你买了一个新书架,但你不确定它能不能放进客厅那个墙角。onMeasure就是帮你量出这个书架(也就是View)自己希望占多大地方(onMeasure中计算出的measuredWidth和measuredHeight),同时也要考虑客厅(也就是父容器)能给的最大空间(MeasureSpec)。这里有个关键点:MeasureSpec是一个32位的int值,高2位代表测量模式(EXACTLY、AT_MOST、UNSPECIFIED),低30位代表具体尺寸。很多面试者会在这里卡壳,不理解为什么一个int能装下两种信息。我当年也是画了好几遍二进制图才搞懂。
测量过程是递归进行的。从最顶层的DecorView开始,它根据手机屏幕尺寸和Window的尺寸限制,生成自己的MeasureSpec,然后一层层向下传递,直到每一个最底层的View。子View的测量结果又会一层层向上汇报,最终确定整个视图树每个节点的实际尺寸。这里有个实战中的坑:自定义View时,如果你没有正确重写onMeasure并处理wrap_content的情况,你的View很可能在设置为wrap_content时,表现得和match_parent一样,铺满整个父容器。解决方法是,在onMeasure里,当测量模式是AT_MOST时,给自己设定一个默认的、合理的尺寸。
onLayout的定位艺术。量好尺寸后,就该摆位置了。onLayout方法对于ViewGroup(比如LinearLayout、RelativeLayout)至关重要,它决定了每个子View应该放在自己的哪个坐标上。这个方法接收四个参数:l(left)、t(top)、r(right)、b(bottom),这就是该ViewGroup在父容器中的“地盘”。它的任务就是在这个地盘里,根据子View测量出来的大小,以及自身的布局规则(比如垂直排列、水平居中),计算出每个子View的具体坐标,然后调用子View的layout()方法把位置“派发”下去。对于普通的View(非ViewGroup),onLayout通常是个空实现,因为它没有孩子需要安排。
onDraw的画笔与画布。最后一步就是画画。onDraw(Canvas canvas)方法拿到了一个画布,你可以用Paint画笔在上面绘制任何东西:文字、图形、图片。这里有个非常重要的面试考点:整个绘制流程中,所有View使用的是同一个Canvas吗? 答案是肯定的。这个Canvas从ViewRootImpl的performTraversals()开始,一路传递下来,贯穿整个视图树的绘制。它就像一卷长长的画布

211

被折叠的 条评论
为什么被折叠?



