笔记简单介绍
本篇文章主要用于梳理自己的思路,从View的绘制时机,到View的绘制流程进行梳理。
View的绘制时机
绘制时机简单介绍
Android中每一个Activity都会包含一个Window进行视图的装载,而Window则会包含一个DecorView用于加载用户设置的contentView,而DecorView会通过一个ViewRootImpl进行WindowManager的关联,从而执行View的一系列事件。
Activity与Window的关联
activity启动过程中会在performLaunchActivity中调用attach方法将PhoneWindow即Window的子类加为自己的成员变量,
WindowManager与DecorView的关联
自定义Activity时调用setContentView后,其方法内部会调用window的setContentView方法,此方法中会进行DecorView是否创建的判断,没创建会进行DecorView的创建,创建成功后通过LayoutInflater.inflate方法创建用户设置的布局,创建结束后,当执行到handleResumeActivity时,会执行wm.addView方法进行windowManager与DecorView的关联,此方法中会创建一个ViewRootImpl将WindowManager与DecorView关联起来。关联成功后会通过ViewRootImpl的 performTraversals方法顺序执行performMeasure,performLayout,performDraw方法进行View的绘制逻辑控制
View的绘制逻辑
performMeasure
此方法中会调用View的measure方法并传入两个参数widthMeasureSpec 和 heightMeasureSpec 进行View的测量,MeasureSpec是父View传递到子View的测量模式和测量大小。测量模式包含三种:
- UNSPECIFIED:未指定模式,即父view不限制子view的大小。
- EXACTLY:指定模式,即父view指定了子view的大小,当设置match_Parent时或者设置了指定的大小时,父view会传递此模式
- AT_MOST:最大模式,即父view指定了子view的最大尺寸,当设置为wrap_Content时,父view会传递此模式给子view。
android 中通过MeasureSpec静态类获取父view传递的测量模式以及测量尺寸。
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
/**
* 根据测量尺寸和测量模式生成measureSpec值,可以用于传递到子View
**/
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int makeSafeMeasureSpec(int size, int mode) {
if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
return 0;
}
return makeMeasureSpec(size, mode);
}
/**
* 根据父view传递的measureSpec获取测量模式
**/
@MeasureSpecMode
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
/**
* 根据父view传递的measureSpec获取测量尺寸
**/
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
performLayout
此方法会调用View的layout方法并传入left,top,right,bottom四个参数,分别对应view的四个角的位置。
layout中会进行setframe的方法调用,此方法会判断布局位置是否变更,若变更则进行重新布局。
performDraw
此方法中会执行到View的draw方法进行view的绘制,draw方法中会通过不同层次进行View的绘制
- 绘制自身的背景,通过调用drawBackground()
- 绘制自身的内容,通过调用onDraw(),一般自定义View时实现的就是此方法,绘制自身内容
- 绘制子View,通过调用dispatchDraw(),
- 绘制前景,例如滚动条,滚动指示标等,通过调用onDrawForeground()方法
onDraw中一般使用canvas,paint,path,Matrix进行自定义View内容绘制。
canvas:画布,主要用于展示绘制的内容 paint:画笔,主要与画布相结合,用于绘制内容颜色,样式的设置 path:路径,画笔可以根据path的坐标绘制出不规则的图案。 matrix:矩阵,实现对画布的几何变换
总结
android中的View的绘制流程从ViewRootImpl中的PerformTraversals方法开始,其会按顺序分别调用performMeasure,performLayout,performDraw三个方法进行View的measure,layout,draw执行,其中measure会进行View的大小测量,根据viewRootImpl传递的measureSpec参数。layout中会进行View的坐标设置,draw中会进行背景,内容,子View,前景的绘制,自定义View时一般重写onDraw方法对自身内容绘制。