3、布局与测量:View的measure/layout流程、过度绘制(Overdraw)检测与优化、扁平化布局策略
3.1 View的measure/layout流程——别让系统白干活
做座舱开发的朋友,一定遇到过界面卡顿。尤其是仪表盘或者中控屏,滑动时掉帧,点按有延迟。我排查过很多这类问题,十有八九都出在布局测量阶段。
Android的View绘制,说白了就三步:measure、layout、draw。今天重点聊前两步。
measure是测量阶段。系统会问每个View:「你要多大?」View根据父容器给的约束,算出自己的宽高。然后父容器再根据所有子View的诉求,决定最终尺寸。
layout是布局阶段。系统拿着measure的结果,给每个View分配位置。说白了就是:「你,左上角在这里;你,右下角在那里。」
这里有个坑——measure会触发多次。为什么?因为父容器可能不确定子View到底要多大,需要反复测量。比如RelativeLayout,它需要先测量所有子View,才能确定相对位置关系。这就导致measure次数翻倍。
核心结论:measure次数越多,性能越差。一次布局刷新,如果触发多次measure,UI线程就被占住了。
我在项目中遇到过一个问题:一个仪表盘页面,里面嵌套了四层LinearLayout。每次刷新,measure次数高达200多次。页面滑动时,帧率直接掉到20fps。后来改成ConstraintLayout,measure次数降到40次,帧率回到55fps。
3.2 过度绘制(Overdraw)检测与优化
过度绘制,就是同一个像素点被重复绘制了多次。你想想看,一个像素点本来画一次就够了,结果画了三次、四次,GPU白干活,帧率自然上不去。
怎么检测?Android Studio自带的GPU Overdraw工具,打开后屏幕会变成彩色。颜色越红,说明过度绘制越严重。
- 蓝色:过度绘制1次(正常)
- 绿色:过度绘制2次(可接受)
- 粉色:过度绘制3次(需要优化)
- 红色:过度绘制4次以上(必须优化)
我建议,座舱界面里,红色区域占比不要超过5%。如果超过,用户操作时就能感觉到卡顿。
优化手段有哪些?
- 去掉不必要的背景。很多开发者习惯给每个View都设背景色。其实父容器有背景,子View就不需要再设了。我曾经看到一个页面,三层布局都设了白色背景,过度绘制直接飙到红色。去掉两层后,瞬间变蓝。
- 使用merge标签。如果布局层级固定,用merge可以减少一层ViewGroup。比如自定义组合控件时,用merge代替FrameLayout。
- 裁剪(Clip)。如果某个View只有部分区域需要显示,用clipChildren或clipToPadding裁剪掉不可见部分,避免GPU绘制无用像素。
小技巧:在开发者选项里打开「显示过度绘制区域」,边开发边看。我习惯每写完一个页面,就打开这个开关扫一眼。红色区域超过10%,我就知道该优化了。
3.3 扁平化布局策略——少一层,快一步
布局层级越深,measure和draw的时间就越长。为什么?因为每层ViewGroup都要做测量和布局计算。嵌套五层,计算量就是五倍。
扁平化的核心思路:能用一层解决的,绝不用两层。
举个例子。以前做导航栏,很多人这么写:
<LinearLayout>
<RelativeLayout>
<ImageView />
<TextView />
</RelativeLayout>
</LinearLayout>
其实用ConstraintLayout一层就能搞定:
<androidx.constraintlayout.widget.ConstraintLayout>
<ImageView />
<TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
少了一层RelativeLayout,measure次数直接减半。
具体策略:
| 原方案 | 扁平化方案 | 效果 |
|---|---|---|
| LinearLayout嵌套LinearLayout | ConstraintLayout | 减少1~2层 |
| RelativeLayout + FrameLayout | ConstraintLayout | 减少1层 |
| 多层LinearLayout权重布局 | GridLayout或ConstraintLayout | 减少2~3层 |
嗯,这里要注意:扁平化不是万能的。如果布局逻辑本身复杂,强行压成一层,反而会让ConstraintLayout的约束关系变得复杂,测量时间反而增加。我建议,布局层级控制在3层以内,超过3层就要考虑优化了。
避坑指南:我曾经把一个五层布局硬压成一层,结果ConstraintLayout的约束条件写了20多个,measure时间反而比原来多了30%。扁平化要适度,不是越扁越好。合理的目标是:在满足功能的前提下,层级最少。
3.4 实战建议
总结一下,布局优化的三个核心点:
- 减少measure次数:用ConstraintLayout代替RelativeLayout和LinearLayout嵌套
- 消除过度绘制:去掉冗余背景,使用merge和clip
- 扁平化布局:层级控制在3层以内,但不要过度压缩
我个人的习惯是:每写完一个页面,先开GPU Overdraw扫一遍,再用Layout Inspector看层级。两个工具配合,基本能覆盖90%的性能问题。剩下的10%,就得靠Traceview抓measure耗时了——这个我们后面章节再聊。
记住一句话:布局不优化,后面全白搭。measure和layout是绘制的起点,起点慢了,后面再怎么优化draw也是杯水车薪。