作者博客
http://www.cherylgood.cn
前言
大家好!本次我们将继续学习Android之自定义View的死亡三部曲中的第二部:排兵布阵
我们在上一篇「Android之自定义View的死亡三部曲之Measure」中分析了死亡三部曲的第一部,也是三部中最复杂的一步:View的测量,想知道View的测量相关知识可以点进去查看哦!
通过第一部View的测量,我们就能拿到View的三围数据了(View的宽高)。
那么接下来我们要做的当然就是对测量好的View进行布局了。
开始布局
Ok,说干就干,这次,我们同样是从ViewRootImpl的performTraversals方法开始,还记得我们的performTraversals方法体内部都有哪些内容么?我们再粘贴一下代码吧。
我们上次分析测量是以performMeasure为入口进行分析的,那么本次分析到布局,当然是从performLayout作为起点了。
Ok,那么我们就直接看performLayout方法体内部的源码吧
我们可以看到,在1处,直接调用了host.layout进行布局,而host是什么东东呢?其实host就是我们的DecorView,还记得我们之前分析View的诞生之谜的时候,在创建ViewRootImpl时,直接把DecorView赋值给mView了。
那么也就是说其实是调用了DecorView的layout方法。我们再看下其传递的参数分别是0,0,host.getMeasuredWidth(),host.getMeasuredHeight()
而这四个参数按顺利所代码的含义分别是left,top,right,bottom,也就是左、上、右、下
left、top当然是0了,为什么呢?难道你想手机屏幕显示一个画面是,左边和顶部不是刚好贴合的么?显然不会希望这样,简直丑死啦。
宽就是我们DecorView测量后的宽度,高就是DecorView测量后的高度
Ok,所有的控件当时都是继承自View了,那么我们看下View的layout方法
在1中针对不同的layoutMode调用了不同的方法,我们来看下一班的layoutMode模式下调用setFrame方法时,内部做了什么操作呢
可以看到changed的值只与四个点是否发生了变化有关。
同时,我们还发现,如果你想获得某个view的top、left、right、bottom的值,在layout之后就可以拿到了。
而从View.layout方法的2位置处我们知道,在执行了setFrame之后调用的是onLayout方法,所以也就是说,我们可以在onLayout方法中获得四个位置点的值。
View类的成员变量mLeft、mRight、mTop和mBottom分别用来描述当前视图的左右上下四条边与其父视图的左右上下四条边的距离,如果它们的值与参数left、right、top和bottom的值不相等,那么就说明当前视图的大小或者位置发生变化了。这时候View类的成员函数setFrame就会将参数left、right、top和bottom的值分别记录在成员变量mLeft、mRight、mTop和mBottom中。
然后我们很开心的点开了View.onLayout方法,发现,居然是空的!~~空的!
没错,就是空的,一般该方法是用来确认childView的位置的,比如FrameLayout会调用onLayout方法告知childView,你可以可以开始布局了哦。然后childView就会调用自身的layout方法完成自身的布局工作,如果childView中还包含有childView,就会一直调用下去。
我们先来梳理下流程:
- performTraversals内部调用performLayout开始执行布局工作
- performLayout内部会调用layout开始进行布局
- layout中会调用setFrame确定mTop,mLeft,mRight,mBottom的值以及判断是个点的值是否发生了变化
- 最后调用onLayout方法通知下面的childView进行布局操作
ok,那么我们就分析下FrameLayout的onLayout方法
从上面可以看到内部只是调用了layoutChildren方法,layoutChildren才是具体的实现
我们继续看下layoutChildren里面的代码:
知识点梳理:
- 获取父View的内边距padding的值
- 遍历子View,处理子View的layout_gravity属性、根据View测量后的宽和高、父View的padding值、来确定子View的布局参数,
- 调用child.layout方法,对子View进行布局
对childView进行布局
从上面的分析我们的可以知道,如果子view属于FrameLayout这种布局类的View,里面就会重复上面流程,如果不是,最终就会调用到View.onLayout,而这个方法是一个空的实现,所以我们在自定义View时,需要重新onLayout实现布局的操作
总结:
布局流程主要的操作就是确定View的四个点的数值,相对于之前的测量,是不是要简单一些呢?
关注微信公众号「码个蛋」,每天更新优质文章