在Android面试中,面试官经常会问:讲讲View的绘制流程吧,但对于很多刚开始找实习的人来说,要详细介绍这个绘制流程还是很吃力的,所以我就简单介绍其中的核心内容帮助大家简单过一下。
View的绘制流程是Android系统中UI显示的核心机制,主要分为三个阶段:测量(Measure)、布局(Layout)和绘制(Draw)。这三个阶段从根View开始,递归地作用于整个View树,确保每个View都能正确计算大小、确定位置并最终渲染到屏幕上。
1. 测量(Measure)阶段
测量阶段的目的是确定View的宽高。系统从根View开始,调用每个View的measure()
方法。measure()
方法会根据父View传递的MeasureSpec
(测量规格)和View自身的LayoutParams
(布局参数)来计算宽高。MeasureSpec
包含模式(mode)和尺寸(size),模式有三种:UNSPECIFIED
(未指定,通常用于系统内部)、EXACTLY
(精确值,如固定宽高或match_parent)和AT_MOST
(最大值,如wrap_content)。在measure()
中,View会调用onMeasure()
方法,通过setMeasuredDimension()
保存测量结果。对于普通View,onMeasure()
通常直接使用默认逻辑;而对于ViewGroup,它还需要在onMeasure()
中递归测量所有子View,并结合自身的布局规则确定最终宽高。
2. 布局(Layout)阶段
布局阶段的任务是确定View在屏幕上的具体位置。系统同样从根View开始,调用每个View的layout()
方法。layout()
方法接收四个参数:左上角坐标(left, top)和右下角坐标(right, bottom),这些坐标是相对于父View的。View在此阶段保存自己的位置信息。对于普通View,onLayout()
通常为空,普通View没有子View需要布局,它只需要保存自己的位置信息即可;而对于ViewGroup,它需要在onLayout()
中为每个子View计算位置,并调用子View的layout()
方法,从而完成整个View树的布局。
3. 绘制(Draw)阶段
绘制阶段是将View内容渲染到屏幕上的过程。系统从根View开始,调用每个View的draw()
方法。draw()
方法内部按顺序执行以下步骤:绘制背景、调用onDraw()
绘制自身内容、绘制子View(如果是ViewGroup)以及绘制装饰(如滚动条)。这一阶段完成后,View的内容就显示在屏幕上了。
调用 requestLayout() 会重新走测量与布局流程,调用 invalidate() 则只触发重绘,确保界面及时更新。
开发者通常通过重写onDraw()
方法,使用Canvas
和Paint
工具实现自定义绘制逻辑,例如绘制图形或文本。
整体流程与调度
View的绘制流程由ViewRootImpl
负责调度。当UI需要更新时(如Activity启动或用户交互),ViewRootImpl
会调用performTraversals()
方法,依次触发测量、布局和绘制三个阶段。整个过程以深度优先的方式遍历View树,确保所有View按顺序完成绘制。
实际开发中的注意事项
在开发中,我们通常无需手动调用measure()
、layout()
或draw()
,这些由系统自动完成。但对于自定义View,可能需要重写onMeasure()
定义宽高逻辑、重写onLayout()
调整子View位置,或重写onDraw()
实现个性化绘制。