View 绘制流程深度解析
从 measure、layout 到 draw,完全掌握 View 渲染原理
目录
绘制流程概述
Measure 测量阶段
Layout 布局阶段
Draw 绘制阶段
整体流程图
面试高频问题
1. 绘制流程概述 三大核心方法
flowchart TB
Measure["measure(int, int)<br/>确定 View 的宽高"] --> Layout["layout(int, int, int, int)<br/>确定 View 的位置"]
Layout --> Draw["draw(Canvas)<br/>绘制 View 内容"]
Draw --> Order["调用顺序:measure → layout → draw"]
何时触发绘制 1 2 3 4 view.requestLayout() view.invalidate() view.postInvalidate()
2. Measure 测量阶段 MeasureSpec 详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 val specMode = MeasureSpec.getMode(measureSpec)val specSize = MeasureSpec.getSize(measureSpec)when (specMode) { MeasureSpec.EXACTLY -> println("精确大小: $specSize " ) MeasureSpec.AT_MOST -> println("最大不能超过: $specSize " ) MeasureSpec.UNSPECIFIED -> println("无限制" ) }
onMeasure 核心逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 override fun onMeasure (widthMeasureSpec: Int , heightMeasureSpec: Int ) { val width = getDefaultSize(suggestedMinimumWidth, widthMeasureSpec) val height = getDefaultSize(suggestedMinimumHeight, heightMeasureSpec) setMeasuredDimension(width, height) } public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break ; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break ; } return result; }
自定义 View 的 onMeasure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class CustomView @JvmOverloads constructor ( context: Context, attrs: AttributeSet? = null , defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val defaultWidth = 200 private val defaultHeight = 200 override fun onMeasure (widthMeasureSpec: Int , heightMeasureSpec: Int ) { val widthMode = MeasureSpec.getMode(widthMeasureSpec) val widthSize = MeasureSpec.getSize(widthMeasureSpec) val heightMode = MeasureSpec.getMode(heightMeasureSpec) val heightSize = MeasureSpec.getSize(heightMeasureSpec) val finalWidth = when (widthMode) { MeasureSpec.EXACTLY -> widthSize MeasureSpec.AT_MOST -> minOf(defaultWidth.dp.toPx(), widthSize) else -> defaultWidth.dp.toPx() } val finalHeight = when (heightMode) { MeasureSpec.EXACTLY -> heightSize MeasureSpec.AT_MOST -> minOf(defaultHeight.dp.toPx(), heightSize) else -> defaultHeight.dp.toPx() } setMeasuredDimension(finalWidth.toInt(), finalHeight.toInt()) } }
ViewGroup 的 onMeasure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 override fun onMeasure (widthMeasureSpec: Int , heightMeasureSpec: Int ) { for (i in 0 until childCount) { val child = getChildAt(i) measureChild(child, widthMeasureSpec, heightMeasureSpec) } setMeasuredDimension(measuredWidth, measuredHeight) } protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec( parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width ); final int childHeightMeasureSpec = getChildMeasureSpec( parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height ); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
3. Layout 布局阶段 onLayout 核心逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 override fun onLayout (changed: Boolean , left: Int , top: Int , right: Int , bottom: Int ) { for (i in 0 until childCount) { val child = getChildAt(i) val width = child.measuredWidth val height = child.measuredHeight val childLeft = left + paddingLeft val childTop = top + currentHeight + marginTop val childRight = childLeft + width val childBottom = childTop + height child.layout(childLeft, childTop, childRight, childBottom) currentHeight += height + marginTop + marginBottom } }
setFrame / setOpticalFrame 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false ; if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true ; mLeft = left; mTop = top; mRight = right; mBottom = bottom; if (mRight - mLeft != getWidth() || mBottom - mTop != getHeight()) { mPrivateFlags |= PFLAG_SIZE_CHANGED; } } return changed; }
4. Draw 绘制阶段 draw 核心流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void draw(Canvas canvas) { drawBackground(canvas); onDraw(canvas); dispatchDraw(canvas); onDrawForeground(canvas); }
dispatchDraw 分发绘制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 protected void dispatchDraw(Canvas canvas) { for (int i = 0 ; i < childrenCount; i++) { View child = getChildAt(i); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { drawChild(canvas, child, drawingTime); } } } protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this , drawingTime); }
自定义 View 绘制示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class CircleView @JvmOverloads constructor ( context: Context, attrs: AttributeSet? = null , defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.RED style = Paint.Style.FILL } private var centerX = 0f private var centerY = 0f private var radius = 0f override fun onSizeChanged (w: Int , h: Int , oldw: Int , oldh: Int ) { super .onSizeChanged(w, h, oldw, oldh) centerX = w / 2f centerY = h / 2f radius = minOf(w, h) / 2f - 20f .dp.toPx() } override fun onDraw (canvas: Canvas ) { super .onDraw(canvas) canvas.drawCircle(centerX, centerY, radius, paint) } }
5. 整体流程图
flowchart TB
Request["requestLayout()"] --> Traversal["performTraversals()"]
Traversal --> Measure["measure()"]
Measure --> MDetail["onMeasure() / setMeasuredDimension() / ViewGroup 测量子 View"]
MDetail --> LayoutStage["layout()"]
LayoutStage --> LDetail["setFrame() / onLayout() / ViewGroup 布局子 View"]
LDetail --> DrawStage["draw()"]
DrawStage --> DDetail["drawBackground() / onDraw() / dispatchDraw() / onDrawForeground()"]
DDetail --> Finish["绘制完成"]
6. 面试高频问题 Q1: requestLayout vs invalidate 区别
方法
作用
触发
requestLayout()
重新 measure + layout + draw
位置或大小变化
invalidate()
重新 draw
内容变化
postInvalidate()
线程中调用 invalidate
同上
Q2: View 测量模式如何决定?
flowchart TB
ParentSpec["父 MeasureSpec + 子 View LayoutParams"] --> ExactParent["EXACTLY"]
ExactParent --> E1["match_parent → EXACTLY"]
ExactParent --> E2["wrap_content → AT_MOST"]
ExactParent --> E3["200dp → EXACTLY"]
ParentSpec --> AtMostParent["AT_MOST"]
AtMostParent --> A1["match_parent → AT_MOST"]
AtMostParent --> A2["wrap_content → AT_MOST"]
AtMostParent --> A3["200dp → EXACTLY"]
ParentSpec --> UnspecifiedParent["UNSPECIFIED"]
UnspecifiedParent --> U1["任意 → UNSPEC"]
Q3: 为什么 wrap_content 不生效? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 override fun onMeasure (widthMeasureSpec: Int , heightMeasureSpec: Int ) { super .onMeasure(widthMeasureSpec, heightMeasureSpec) val desiredWidth = 200. dp.toPx() val desiredHeight = 200. dp.toPx() val width = resolveSize(desiredWidth, widthMeasureSpec) val height = resolveSize(desiredHeight, heightMeasureSpec) setMeasuredDimension(width, height) }
总结
flowchart TB
DrawCore["View 绘制核心"] --> D1["Measure: measure() → onMeasure() → setMeasuredDimension()"]
DrawCore --> D2["Layout: layout() → onLayout() → setFrame()"]
DrawCore --> D3["Draw: draw() → onDraw() → dispatchDraw()"]
相关文章 :