您的位置:首页 > 移动开发 > Android开发

Android 自定义View 之测量过程(onMeasure)

2016-12-17 16:33 375 查看
       本文探究测量的全过程,不是简单的介绍onMeasure()函数!

一、测量的目的

      

        如果安卓的布局体系中全部都是精确的值,那就没有必要关心测量过程了。事实上,在布局文件中写的布局参数都是match_parent,wrap_parent或者精确值。

       测量的目的就是为了将match_parent,wrap_parent这些相对大小,转换为具体的值。

二、测量的依据

        应用开发者有三种方式表示长宽:match_parent,wrap_parent,精确值

         测量体系内部有三种模式:EXACTILY,AT_MOST,UNSPECIFIED

       其转换所对应的表格如下:

paretMeasureSpec
Lp.height
childMeasureSpec
EXACTLY+SIZE
具体值
EXACTLY+具体值
同上
WRAP_CONTENT
AT_MOST+size
同上
MATCH_PARENT
EXACTLY+size
AT_MOST+SIZE
具体值
EXACTLY+size
同上
WRAP_CONTENT
AT_MOST+size
同上
MATCH_PARENT
AT_MOST+size
UNSPECIFIED+SIZE
childDimension
EXACTLY+childDimension
同上
WRAP_CONTENT
UNSPECIFIED+0
同上
MATCH_PARENT
UNSPECIFIED+0
    测量规格即表中的MeasureSpec,其组成为:测量模式+大小。
    测量模式有三种:

    EXACTILIY:父视图期待子视图的最理想大小

    AT_MOST:父视图允许子视图的最大值

    UNSEPECFIED:好吧,我不知道这个做啥用的,基本没遇到过

    上述表格意义解释:

    paretMeasureSpec:这是测量体系传递下来的,由系统转换完成

    Lp.height:对应XML文件中latyou_height = "XXXX",这里是由程序猿指定。

    ChildMeasureSpec :经过表的转换后得出自己的测量模式和测量中值,如果当前测量控件是一个ViewGroup,这个值还会传递给子控件。

    

三、测量过程

      整个的测量过程,根据个人的理解做了一个图,将就着看:

      


   将视图分为两类:View和ViewGroup

   1->      ViewGroup有两个动作:

    动作一:measure(父控件给定的宽规格,父控件给定的高规格)  函数接受来自父控件指定的测量规格,然后结合自己的布局参数           (layout_width,layou_height)来测量自身的宽和高 ,测量模式。这个时候就用到了最上面的表格,这一步的时候,measure中会回调onMeasure();

    这就是View中的onMeasure()函数中的参数是怎么来的了。

   动作二:

    根据padding等属性,综合进行考虑,给每个子控件分配测量规格

    

    2->   View相对简单,只有一个操作:

     只有动作一:根据父控件传递下来的宽高测量规格,得出自己的宽高。同样的也会回调onMeasure()方法。

    

     在实际的使用过程,一遍的控件都有父控件,有父控件提供测量规格进行参考,那根视图呢?

      根视图有单独的计算方法,其大小需要结合窗口的布局参数。一般情况,根视图的measure方法所获得测量规格为Exactly+窗口大小!

注意:

    以上测量的过程都是系统完成,只有onMeasure()这一步应用程序可以参与。而且各种规格都只是建议,应用程序的代码可以通过     setMeasureDimension()来一票否决,直接指定当前控件的大小。

    同时,不建议直接继承ViewGroup,通过上述的过程,应该知道,如果直接继承ViewGroup你就需要处理给子空间分配大小的过程,这个过程肯定麻烦。

大多数的开源控件中也很有直接继承ViewGroup,要么是继承的View,要么就是继承已有的VIewGroup子类,像LinearLayout之类的。

四、实际的运用过程。

        一个窗口承载一个视图,视图最终会表现为一个View构成的树。每个节点(也就是View)必须拥有LayoutParam的属性。

来看看View的获得方式:

方式一:new 一个View

方式二:通过 LayoutInflater.from(this).inflate(R.layout.XXX,null);的方式来构造出一个View

        如果你调用getLayoutParams(),会发现它们的布局参数都是空的。

方式一不奇怪,你本来就没有设置布局参数,自然没有

方式二奇怪的很,不是布局XML里面一般都有写么!其实是构造的过程最外层的ViewGroup的宽高属性是没有生效的,如果需要使得它生效,需要调用LayoutInfalter.from(this).infate的另外一种用法。

既然没有布局参数,它们最后都是怎么测量的?

其实,以上的view都是孤立的View,如果需要让它可见,一定要把通过addView的方法,加入主干的视图树中。查看addView这个方法会发现有个特点,如果你加入的View没有布局参数,它会生成一个默认的参数。如果有就使用子控件的布局参数。

这里有一点需要注意到 是,如果给一个LinearLayout通过addView的方式添加一个View,而这个View你通过setLayoutParams的方式指定了一个布局参数,这个指定的布局参数必须是LinearLayout.LayoutParams ,不然就会报类型错误。

如下图所示,每个VIew所对应的LayoutParams类型是是父母控件的类型的布局参数!



    实际的运用就是,需要对控件本身定制大小,不受父母控件的干扰,就手动的设置精确布局参数,需要注意其类型,类型一旦确实就只能添加到某个指定的控件下了。

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐