Android View的绘制流程(1) -- 测量onMeasure

鉴于是首篇讲解自定义view流程,之前也在网上搜了一些博主的博客看了看,都是大同小异,今天抽时间自己总结一下,分享一下自己的感悟,也算是一篇笔记。

(本篇为开头篇,稍微讲述一下有关的东西)

    View的绘制流程:

       a: Measure测量一个View的大小(onMeasure) (本篇讲解Measure 测量)

       b: Layout  摆放一个View的位置(onLayout)

       c: Draw 绘制  View的内容 (onDraw)

    举个例子:假如你想盖一所房子,首先你需要确定你盖的房子占地是多少平米,高几米(Measure)

    确定好尺寸后,你需要定位,找个合适的地方盖这所房子,是想盖天安门旁边,还是盖到老家后院。(Layout)

    当你确定好房子的宽高大小,占地尺寸后(Measure后),也想好房子在哪里盖(老家后院 Layout后),那就该

    搭建房子,开始盖房子了(Draw)。

    

    附上两张截图,看一下Log打印  了解一下执行顺序:

重写三个方法

控制台打印

应该会有人有两个疑问:

    1:为什么在view.class类中,绘制方法用的是onMeasure 而不是 Measure?

    2:为什么控制台打印中  有两个 onMeasure?为什么会走两次

   回答:

 1:

继承的View中的measure方法

由图可见,因为measure方法是final类型的  所以无法被继承,而layout 和  draw方法是void类型。那为什么是重新onMeasure方法呢?是因为官方在measure方法上方的注解中提到的:看下图

measure方法上方注解

    注解的大概意思是:这方法(measure() )是一个查找view多大,一个父类提供了约束信息在宽高方面(也就是说子类是由父类提供约束信息的)它实际测量的工作 是在 onMeasure()中完成的.  

所以第一个问题解答完毕!

问题2:答案(为什么打印有两个onMeasure() ),的确,onMeasure() 方法执行了两遍,其实layout 与 draw方法也有可能会走多遍,至于走不走多遍,是根据视图的复杂性决定的,视图越复杂,测量,定位,绘制三个方法走的次数就会增多。

                                  ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓     ↓

    进入正题: Measure (onMeasure (测量View大小)

     

        

测量View大小方法

       Measure方法中调用了OnMeasure方法,实际测量是在OnMeasure方法中完成的,所以我们直接看OnMeasure方法

       可以看到OnMeasure方法有两个参数 widthMeasureSpec 和 heightMeasureSpec。 

       widthMeasureSpec  :父容器对子类的宽度的期望

       heightMeasureSpec:父容器对子类的高度的期望

    

onMeasure中调用的setMeasuredDimension方法

       进入onMeasure方法中 看到在onMeasure方法中 调用了 setMeasuredDimension方法。

       然后我们继续进入到setMeasuredDimension方法中 去看一下里面有什么操作。   

setMeasuredDimensionRaw方法中的内容

            可以看到在setMeasuredDimension方法中 仅仅只是把测量出来的宽高,然后在setMeasuredDimensionRaw()方法中进行了保存。                                            

所以 测量的流程就是 Measure -->  onMeasure -- > setMeasuredDimension -- >  setMeasuredDimensionRaw.

                                         ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓    ↓     ↓

测量:至于测量view大小的measure中 有一个必不可少的系统自带的工具类 MeasureSpec类

        MeasureSpec类:(获取view的测量模式以及view想要绘制的大小)

                1:measureSpec描述了父View 对 子View大小的期望,里面包含测量模式和大小

                2:MeasureSpec类 将测量模式与测量大小组合到一个32位的int类型的数值中,高两位表示测量模式,低30位代表测量大小,在计算中使用位运算的原因是提高并优化效率

                3:可以从MeasureSpec类中提取测量模式与大小,内部是采用了位运算的方式,也可以通过MeasureSpec的静态方法把大小和模式合成,该方法内部只是简单的相加。

    

measure方法中打印宽度

上图举了个例子,打印了一下宽度的值,得到的这个数据 就是测量模式与大小,拼在一起的数据。应着了上面的第二点,将测量模式和测量大小组合到一个32位的int类型的数值中。

测量模式:(分为三种)

1:EXACTLY(精准值模式),当控件的layout_width或layout_height属性定位具体数值时,或指定为match_parent属性时,系统使用的是EXACTLY模式

2:AT_MOST(最大值模式),当空间的layout_width或layut_height属性指定为warp_content时,控件大小一般随着控件的子控件或者内容的变化而变化,这时控件的尺寸只要不超过父控件允许的最大尺寸即可。

3:UNSPECIFIED(未定义的模式),它不指定其大小的测量模式,view想多大就多大,通常会在自定义view时使用。

注:View的onMeasuer方法只支持EXACTLY模式,所有如果在自定义控件的时候不重写onMeasure()的话,就只能使用EXACTLY模式,且控件只能响应你指定的具体宽高值或者match_parent属性,如果要让自定义的View支持wrap_content属性,那就必须重新onMeasure方法来指定wrap_content时的大小。

measureSpec类的作用是获取测量模式以及大小,所以这个类中有两个方法,getMode()和getSize()

在xml中使用时,宽的大小已指定

打印了一下宽的size大小和宽的mode模式

问题:

1:为什么我代码中打印log 要 mode>>30?  为什么打印结果是1 ?

2:为什么size是600? 在xml中宽是200dp呀

答:

1:因为刚刚说到measure()方法中的两个宽高值,是int类型 32位的 由2位的测量模式和30位的大小组合而成,所以需要将值往前移30位,拿到前两位也就是测量模式。所以打印的是结果为 1  这个1 可以看上图 红框框住的三个测量模式,源码中可以看到,精准模式EXACTLY 为1  ,AT_MOST 最大值模式为2,UNSPECIFIED未定义模式为0,所以我们打印的测量模式为 1,相对应的就是精准模式,在看我们在xml中设置的宽的大小为200dp,是指定的大小,所以就是精准模式。也就是打印的值为 1。

2:xml中为200dp 为什么size是600? 因为这个600是px 也就是像素,遵循一个公式,

px(像素) =  dp  *  dpi(屏幕像素密度) / 160 (固定为160,不会改变) ,现在已知px(像素)  ,我的模拟器dpi为480,求dp数  不难吧?

模拟器dpi

像素px(600) = dp(未知) * 480 / 160;  求dp值。

dp =  px(600) / 480 * 160  == 200  所以dp = 200 与 xml相对应。这只是验算一下。

你也可以假设px(log控制台打印的像素) = 200(已知xml中的宽度) *dpi (运行的手机屏幕像素密度)/ 160 看结果是否和控制台打印的一致。

以上就是measure 测量的过程。有什么问题可以留言或私信。谢谢

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值