View的工作流程概述

1. 概述

  • View的工作流程主要指measure(确定iew的测量宽/高),layout(确定view的最终宽/高和四个顶点位置),draw(将view绘制到屏幕上)这三大流程.
  • View的三大流程均通过ViewRoot来完成,而ViewRoot对应于ViewRootImpl类,是连接WindowManager和DecorView(是一个FramLayout,为顶级view,其id为android.R.id.content)的纽带.
  • View的绘制流程从ViewRoot的performTraversals方法开始
    • measure: 测量View的宽和高, 调用onMeasure对子控件进行measure
    • layout: 确定View在父容器的放置位置
    • draw: 将View显示在屏幕上
  • 图示:

1.1 measure过程

  • 分类:
    • View :通过measure方法完成
    • ViewGroup :除了完成自己的测量过程,还会遍历调用所有子view的measure方法,子view在递归执行
1.1.1 关于MeasureSpec
  • 简介: 通常翻译为”测量规格”,它是一个32位的int数据.
    • 高2位代表SpecMode即某种测量模式,
    • 低30位为SpecSize代表在该模式下的规格大小.
       
  • 模式(SpecMode):3种,见下表
模式(int类型) 高2位数值 描述
UNSPECIFIED 00 = 0 << 30 默认值,父控件没有给子view任何限制,子View可以设置为任意大小
EXACTLY 01 = 1 << 30 表示父控件已经确切的指定了子View的大小
AT_MOST 10 = 2 << 30 表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小
  • 注意:如果对View的宽高进行修改了,不要调用super.onMeasure(widthMeasureSpec,heightMeasureSpec);要调用setMeasuredDimension(widthsize,heightsize); 这个函数。

1.2 layout过程

  • 简介:ViewGroup用来确定子控件的位置
  • 当ViewGroup的位置被确定后,在onLayout中遍历所有的子元素并调用其layout方法,然后调用onLayout,如此遍历

1.3 draw过程

  • 将view绘制到屏幕上

2. 工具方法:

  • View中提供的相关工具方法

    • View.getDefaultSize(int size, int measureSpec)

      public static int getDefaultSize(int size, int measureSpec) {
      //size表示的是View想要的尺寸信息,比如最小宽度或最小高度
      int result = size;
      //从measureSpec中解析出specMode信息
      int specMode = MeasureSpec.getMode(measureSpec);
      //从measureSpec中解析出specSize信息,不要将specSize与上面的size变量搞混
      int specSize = MeasureSpec.getSize(measureSpec);
      
      switch (specMode) {
      //如果mode是UNSPECIFIED,表示View的父ViewGroup没有给View在尺寸上设置限制条件
      case MeasureSpec.UNSPECIFIED:
          //此处当mode是UNSPECIFIED时,View就直接用自己想要的尺寸size作为量算的结果
          result = size;
          break;
      //如果mode是UNSPECIFIED,那么表示View最大可以取其父ViewGroup给其指定的尺寸
      //如果mode是EXACTLY,那么表示View必须使用其父ViewGroup指定的尺寸
      case MeasureSpec.AT_MOST:
      case MeasureSpec.EXACTLY:
          //此处mode是UNSPECIFIED或EXACTLY时,View就用其父ViewGroup指定的尺寸作为量算的结果
          result = specSize;      
          break;
      }
      return result;
      }
    • View.combineMeasuredStates(int curState, int newState): 合并两种状态
    • View.resolveSize(int size,int measureSpec): 主要作用就是根据你提供的大小和模式,返回你想要的大小值(int类型)
    • View.resolveSizeAndState(int size, int measureSpec, int childMeasuredState)
      public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
      final int specMode = MeasureSpec.getMode(measureSpec);
      final int specSize = MeasureSpec.getSize(measureSpec);
      final int result;
      switch (specMode) {
          case MeasureSpec.AT_MOST:
              if (specSize < size) {
                  //当specMode为AT_MOST,并且父控件指定的尺寸specSize小于View自己想要的尺寸时,
                  //我们就会用掩码MEASURED_STATE_TOO_SMALL向量算结果加入尺寸太小的标记
                  //这样其父ViewGroup就可以通过该标记其给子View的尺寸太小了,
                  //然后可能分配更大一点的尺寸给子View
                  result = specSize | MEASURED_STATE_TOO_SMALL;
              } else {
                  result = size;
              }
              break;
          case MeasureSpec.EXACTLY:
              result = specSize;
              break;
          case MeasureSpec.UNSPECIFIED:
          default:
              result = size;
          }
      return result | (childMeasuredState & MEASURED_STATE_MASK);
      }
    • 调用时机:
      • Android中的许多layout类都调用了resolveSizeAndState方法,比如LinearLayout在量算过程中会调用resolveSizeAndState方法而非getDefaultSize方法。
      • 我们自己在实现自定义的View或ViewGroup时,我们可以重写onMeasure方法,并在该方法内调用resolveSizeAndState方法。
    • shouldDelayChildPressedState :返回false,其作用是告诉framework我们当前的布局不是一个滚动的布局

      The default implementation returns true for compatibility reasons. Subclasses that do not scroll should generally override this method and return false.

标签: CustomeView, Android

添加新评论