Android 的绘制流程整理

108 阅读3分钟

笔记简单介绍

本篇文章主要用于梳理自己的思路,从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方法并传入两个参数widthMeasureSpecheightMeasureSpec 进行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的绘制

  1. 绘制自身的背景,通过调用drawBackground()
  2. 绘制自身的内容,通过调用onDraw(),一般自定义View时实现的就是此方法,绘制自身内容
  3. 绘制子View,通过调用dispatchDraw(),
  4. 绘制前景,例如滚动条,滚动指示标等,通过调用onDrawForeground()方法

onDraw中一般使用canvas,paint,path,Matrix进行自定义View内容绘制。

canvas:画布,主要用于展示绘制的内容 paint:画笔,主要与画布相结合,用于绘制内容颜色,样式的设置 path:路径,画笔可以根据path的坐标绘制出不规则的图案。 matrix:矩阵,实现对画布的几何变换

总结

android中的View的绘制流程从ViewRootImpl中的PerformTraversals方法开始,其会按顺序分别调用performMeasureperformLayoutperformDraw三个方法进行View的measurelayoutdraw执行,其中measure会进行View的大小测量,根据viewRootImpl传递的measureSpec参数。layout中会进行View的坐标设置,draw中会进行背景内容子View,前景的绘制,自定义View时一般重写onDraw方法对自身内容绘制。