View绘制(一) performTraversals

View的绘制是一个递归的过程,父View绘制自己和子View,然后子view绘制自己和自己的子View。

我们知道递归的一般数学表达是A1=T,An=f(An-1),那么与之对应,View绘制的A1和函数f()又是什么呢?

答案 :View绘制的A1是DecorView,它是View绘制的起始节点,View绘制的f()函数是measure,layout,draw三大过程,通过递归调用这三个方法完成View的绘制。

在介绍View的绘制过程之前,先介绍几点关于View的相关概念,方便大家理解。

一、View相关概念

View绘制(一) performTraversals_第1张图片
屏幕布局层次图
  1. PhoneWindow:继承自Window类,负责管理界面显示以及事件响应,每个Activity 界面都包含一个PhoneWindow对象,它是Activity和整个View系统交互的接口。

  2. DecorView:是PhoneWindow中的起始节点View,继承自View类,是作为整个视图容器来使用的,主要负责设置窗口属性。

  3. ViewRoot:在系统启动一个Activty组件的同时将其创建,类似于MVC模型中的Controller,负责管理、布局和渲染窗口UI等事务。

二、View绘制开始

View绘制(一) performTraversals_第2张图片
View绘制开始
  1. 系统启动一个Activity的同时创建一个ViewRoot实例。

  2. Activity 在attach阶段生成一个PhoneWindow对象,它包含一个DecorView对象。

  3. 在Activity执行onCreate中的setContentView之后,将读入的view加载进入第一张图的ContentViews区域。

  4. 加载完毕后触发ViewRoot中的scheduleTraversals异步函数,从而进入ViewRoot的performTraversals函数,View的绘制从这里开始。

三、performTraversals

View绘制(一) performTraversals_第3张图片
performTraversals

ViewRoot中的performTraversals方法以DecorView为父容器(ViewGroup)开始自上而下的View工作流程。

View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局和绘制,其中measure确定View的测量宽和高,layout确定View的最终宽/高和四个顶点位置,而draw则将View绘制到屏幕上。

关于measure,layout,draw的具体内容可以看后面的文章,这里不用多在意。这个函数的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小(measure)、是否重新放置视图的位置(layout)、以及是否重绘 (draw),其核心也就是通过判断来选择顺序执行这三个方法中的哪个。

private void performTraversals() {

//1 处理mAttachInfo的初始化,并根据resize、visibility改变的情况,给相应的变量赋值。
  final View host = mView;//这里的mView即DecorView
  final View.AttachInfo attachInfo = mAttachInfo;
  final int viewVisibility = getHostVisibility();
  boolean viewVisibilityChanged = mViewVisibility != viewVisibility
          || mNewSurfaceNeeded;
  float appScale = mAttachInfo.mApplicationScale;
  WindowManager.LayoutParams params = null;
  if (mWindowAttributesChanged) {
      mWindowAttributesChanged = false;
      surfaceChanged = true;
      params = lp;
  }
  Rect frame = mWinFrame;
  if (mFirst) {
      // For the very first time, tell the view hierarchy that it
      // is attached to the window.  Note that at this point the surface
      // object is not initialized to its backing store, but soon it
      // will be (assuming the window is visible).
      attachInfo.mSurface = mSurface;
      attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
              lp.format == PixelFormat.RGBX_8888;
      attachInfo.mHasWindowFocus = false;
      attachInfo.mWindowVisibility = viewVisibility;
      ......
  } 

//2 如果mLayoutRequested判断为true,那么说明需要重新layout,不过在此之前那必须重新measure。
  if (mLayoutRequested) {
      // Execute enqueued actions on every layout in case a view that was detached
      // enqueued an action after being detached
      getRunQueue().executeActions(attachInfo.mHandler);
      if (mFirst) {
          ......
      } 
  }

//3 判断是否有子视图的属性发生变化,ViewRoot需要获取这些变化。
  if (attachInfo.mRecomputeGlobalAttributes) {
      ......
  }
  if (mFirst || attachInfo.mViewVisibilityChanged) {
      ......
  }


//4 根据上面得到的变量数值,确定我们的view需要多大尺寸才能装下。于是就得measure了,有viewgroup的weight属性还得再做些处理
           // Ask host how big it wants to be
          host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
          mLayoutRequested = true;
      }
  }


//5 measure完毕,接下来可以layout了。
  final boolean didLayout = mLayoutRequested;
  boolean triggerGlobalLayoutListener = didLayout
          || attachInfo.mRecomputeGlobalAttributes;
  if (didLayout) {
      host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);

  }


//6 如果mFirst为true,那么会进行view获取焦点的动作。
  if (mFirst) {
      mRealFocusedView = mView.findFocus();
  }

  boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();


//7 终于,来到最后一步,前面的工作可以说都是铺垫,都是为了draw而准备的。
  if (!cancelDraw && !newSurface) {
      mFullRedrawNeeded = false;
      draw(fullRedrawNeeded)
}

你可能感兴趣的:(View绘制(一) performTraversals)