Android UI测量、布局、绘制

测量Measure:

测量模式

一个int占4个字节,一个字节8位,也就是一个int类型的二进制为32位:

  1. MeasureSpec.UNSPECIFIED:任意大小,基本上没用过;值为0x0<<30(16进制的0左移30位):00 0000000000 0000000000 0000000000;
  2. MeasureSpec.EXACTLY:精确测量,如固定大小或match_parent;值为0x1<<30(16进制的1左移30位):01 0000000000 0000000000 0000000000;
  3. MeasureSpec.AT_MOST:不大于父容器size;如wrap_content;值为0x2<<30(16进制的2左移30位):10 0000000000 0000000000 0000000000;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      
    }

widthMeasureSpec:是一个int类型,其中高2位存了测量模式,后面30位存具体的宽度值;

重要的变量MODE_MASK:

MODE_MASK = 0x3<<30(16进制的3左移30位)=11 0000000000 0000000000 0000000000;

MeasureSpec算法:

其实最后的结果就是size的二进制和mode的二进制进行“或运算”就行了: "size | mode";

public static int makeMeasureSpec(int size, @MeasureSpecMode int mode) {
            
     return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
  1. 假设xml中定义的宽为match_parent,则测量模式mode为MeasureSpec.EXACTLY:01 0000000000 0000000000 0000000000;
  2. 假设父容器宽度size为1080;转换32位二进制为:00000000 00000000 00000100 00111000;
  3. “~”:取反,0变1,1变0;
  4. "&": 与运算,同为1则为1,否则为0;
  5. “|”:或运算,有1则为1,没1为0;
  6.   MODE_MASK = 11 0000000000 0000000000 0000000000
  7. ~MODE_MASK = 00 1111111111 1111111111 1111111111;
  8. a=size & ~MODE_MASK  = 00000000 00000000 00000100 00111000 & 00 1111111111 1111111111 1111111111 =  00000000 00000000 00000100 00111000
  9. b=mode & MODE_MASK = 01 0000000000 0000000000 0000000000 &11 0000000000 0000000000 0000000000 =  01 0000000000 0000000000 0000000000;
  10. "a | b" 其实就是size和mode进行“或运算” =00000000 00000000 00000100 0011100001 0000000000 0000000000 0000000000 =  01 000000 00000000 00000100 00111000;
  11. 这样测量模式和size就放到一个int类型中,其中高2位存了测量模式,后面30位存size的大小;

获取测量模式算法:

直接和“MODE_MASK”进行与运算;

  public static int getMode(int measureSpec) {
            
       return (measureSpec & MODE_MASK);
  }
  1. 假设传的measureSpec为01 000000 00000000 00000100 00111000;
  2. 进行与运算:01 000000 00000000 00000100 00111000 & 11 0000000000 0000000000 0000000000 = 01 0000000000 0000000000 0000000000;
  3. 而01 0000000000 0000000000 0000000000 = MeasureSpec.EXACTLY;
  4. 就得到了测量模式

获取size算法:

直接和 “~MODE_MASK”进行与运算;

public static int getSize(int measureSpec) {
     return (measureSpec & ~MODE_MASK);
}
  1. 假设传的measureSpec为01 000000 00000000 00000100 00111000;
  2. ~MODE_MASK = 00 1111111111 1111111111 1111111111;
  3. 进行与运算:01 000000 00000000 00000100 00111000 &  00 1111111111 1111111111 1111111111 = 00 000000 00000000 00000100 00111000;
  4. 而00 000000 00000000 00000100 00111000 = 1080;
  5. 就得到了具体的size大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      
    }

在super.onMeasure中会先生成默认的测量规则measureSpec;

生成默认的测量规则时,当backaground==null时,会给一个最小size=0;(所以自定义view时,没有重写onMeasure时,不设置backaground时,view不显示);

测量只会发生一次,下次直接从一个long类型(64位)的cache中获取;

onMeasure:

  1. 根据父容器传过来的测量规则,得到父容器测量模式和大小
  2. 获取自己的测量模式
  3. 根据自己的测量模式和父容器的测量模式及父容器的大小来进行判断计算自己的大小
  4. setMeasureDimension(widthSize,heightSize)设置自己最终大小

FrameLayout的测量:遍历所有子view,测量子view,找到最大宽度和最大高度的view,将最大宽和最大高设置给自己,在setMeasureDimension()中设置最终的宽高;

布局Layout:

摆放子view的位置,自己的位置是由父容器摆放的;

  1. ViewRootImpl.performLayout()
  2. 调用onLayout(l,t,r,b)
  3. 调用layoutChild(l,t,r,b):viewgroup才有,view没有
  4. 遍历子View,当子View显示时,计算子view的位置
  5. 计算完后,调用child.layout(计算后的l,t,r,b); 一级一级往下传;

绘制draw:

  1. ViewRootImpl.performDraw()
  2. 调用draw()
  3. 获取Surface
  4. 从Surface中获取Canvas;这个Canvas会一层一层的往子View中传,只有这一个Canvas
  5. 调用view.draw(canvas)
  6. 绘制的顺序:
  7. 绘制背景drawBackaground(canvas)
  8. 绘制自己onDraw(canvas);viewgroup是没有绘制自己的,因为viewgroup里面只负责放子View,不负责放其他信息,只有TextView,ImageView等这种才会绘制自己;
  9. 调用子view.onDraw(canvas);让子View走7、8、9、10
  10. 绘制装饰;如:滚动条等装饰

Android 事件分发机制随记_暮冬一十四的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值