Android的自定义视图

概述

安卓开发进阶的必经之路

为什么要自定义视图

自定义视图的基本方法

自定义查看的最基本的三个方法分别是:onMeasure(),onLayout(),onDraw(); 查看在活动中显示出来,要经历测量,布局和绘制三个步骤,分别对应三个动作:测量,布局和平局。

  • 测量:onMeasure()决定查看的大小;

  • 布局:onLayout()决定查看在一个ViewGroup中的位置;

  • 绘制:的onDraw()决定绘制这个视图。

自定义控件分类

  • 自定义查看:只需要重写onMeasure()和onDraw()

  • 自定义ViewGroup:则只需要重写onMeasure()和onLayout()

自定义视图基础

查看的分类

视图查看主要分为两类

类别 解释 特点
单一视图 即一个视图,如TextView的 不包含子视图
视图组 即多个视图组成的ViewGroup中,如的LinearLayout 包含子视图

查看类简介

  • 查看类是Android的中各种组件的基类,如视图是一个ViewGroup基类

  • 查看表现为显示在屏幕上的各种视图

安卓中的UI组件都由查看,ViewGroup中组成。

  • 查看的构造函数:共有4个

    //如果查看是在Java代码里面new的,则调用第一个构造函数public CarsonViewContext context){supercontext);   } //如果视图是在.XML里声明的,则调用第二个构造函数//自定义属性是从AttributeSet中参数传进来的公共CarsonView语境上下文AttributeSet中ATTRS){ 超级上下文ATTRS);   } //不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView上下文语境,
    
           
    
    
    
    
          
           
    
    
    
    
    
          AttributeSet attrsint defStyleAttr){ supercontextattrsdefStyleAttr);   } // API21之后才使用//不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView上下文语境AttributeSet中的ATTRSINT defStyleAttrINT defStyleRes) {supercontextattrsdefStyleAttrdefStyleRes);
           
    
    
       
       
       
       
          
           
      }public CarsonViewContext context){supercontext);   } //如果视图是在.XML里声明的,则调用第二个构造函数//自定义属性是从AttributeSet中参数传进来的公共CarsonView语境上下文AttributeSet中ATTRS){ 超级上下文ATTRS);   } //不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView上下文语境,
    
           
    
    
    
    
          
           
    
    
    
    
    
          AttributeSet attrsint defStyleAttr){ supercontextattrsdefStyleAttr);   } // API21之后才使用//不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView上下文语境AttributeSet中的ATTRSINT defStyleAttrINT defStyleRes) {supercontextattrsdefStyleAttrdefStyleRes);
           
    
    
       
       
       
       
          
           
      }

AttributeSet中的与自定义属性

 系统自带的浏览可以在XML中配置属性,对于写的好的自定义视图同样可以在XML中配置属性,为了使自定义的视图的属性可以在XML中配置,需要以下4个步骤:

  1. 通过为自定义视图添加属性

  2. 在XML中为相应的属性声明属性值

  3. 在运行时(一般为构造函数)获取属性值

  4. 将获取到的属性值应用到景观

查看视图结构

  1. PhoneWindow是Android的系统中最基本的窗口系统,继承自视窗类,负责管理界面显示以及事件响应。它是活动与景观系统交互的接口

  2. DecorView是PhoneWindow中的起始节点观,继承于视图类,作为整个视图容器来使用。用于设置窗口属性。它本质上是一个的FrameLayout

  3. 的ViewRoot在Activtiy启动时创建,负责管理,布局,渲染窗口UI等等

对于多视图的视图,结构是树形结构:最顶层是一个ViewGroup的ViewGroup以及下可能有多个ViewGroup中或查看,如下图:

一定要记住:无论是测量过程,布局过程还是绘制过程,永远都是从搜索树的根节点开始测量或计算(即从树的顶端开始),一层一层,一个分支一个分支地进行(即树形递归),最终计算整个景观树中各个视图,最终确定整个景观树的相关属性。

Android的坐标系

安卓的坐标系定义为:

  • 屏幕的左上角为坐标原点

  • 向右为X轴增大方向

  • 向下为ÿ轴增大方向

区别于一般的数学坐标系

查看位置(坐标)描述

查看的位置由4个顶点决定的4个顶点的位置描述分别由4个值决定:

请记住:查看的位置是相对于父控件而言的)

  • 上图:子视图上边界到父视图上边界的距离

  • 左:子视图左边界到父视图左边界的距离

  • 下图:子视图下边距到父视图上边界的距离

  • 右:子查看右边界到父视图左边界的距离

位置获取方式

查看的位置是通过view.getxxx()函数进行获取:(以最为例)

 
public final int getTop(){   return mTop ;  }   //其余如下:getLeft();      //获取子查看左上角距父查看左侧的距离getBottom();    //获取子查看右下角距父查看顶部的距离getRight();     //获取子视图右下角距父视图左侧的距离
    



 
 
 

与MotionEvent中get()和getRaw()的区别

// get():触摸点相对于其所所组件坐标系的坐标事件getX();       事件的getY(); // getRaw():触摸点相对于屏幕默认坐标系的坐标事件getRawX();    事件getRawY();





事件getX();       事件的getY(); // getRaw():触摸点相对于屏幕默认坐标系的坐标事件getRawX();    事件getRawY();





安卓中颜色相关内容

Android的支持的颜色模式:以ARGB8888为例介绍颜色定义:

查看树的绘制流程

查看树的绘制流程是谁负责的?

视图树的绘制流程是通过的ViewRoot去负责绘制的,的ViewRoot这个类的命名有点坑,最初看到这个名字,翻译过来是视图的根节点,但是事实完全不是这样,的ViewRoot其实不是查看的根节点,它连视图节点都算不上,它的主要作用是查看树的管理者,负责将DecorView和PhoneWindow“组合”起来,而查看树的根节点严格意义上来说只有DecorView;每个DecorView都有一个的ViewRoot与之关联,这种关联关系是由窗口管理去进行管理的;

观点的添加

 

查看的绘制流程

 

测量

  1. 系统为什么要有措施过程?

  2. 测量过程都干了点什么事?

  3. 对于自适应的尺寸机制,如何合理的测量一颗景观树?

  4. 那么ViewGroup中是如何向子查看传递限制信息的?

  5. 滚动型嵌套的ListView问题?

 

布局

  1. 系统为什么要有布局过程?

  2. 布局过程都干了点什么事?

 

  1. 系统为什么要有画过程?

  2. 绘制过程都干了点什么事?

 

的LayoutParams

ayoutParams翻译过来就是布局参数,子视图通过的LayoutParams告诉父容器(ViewGroup中)应该如何放置自己。从这个定义中也可以看出来的LayoutParams与ViewGroup中是息息相关的,因此脱离的ViewGroup谈的LayoutParams是没有意义的。

事实上,每个的ViewGroup的子类都有自己对应的的LayoutParams类,典型的如LinearLayout.LayoutParams和FrameLayout.LayoutParams等,可以看出来的LayoutParams都是对应的ViewGroup子类的内部类

MarginLayoutParams

MarginLayoutParams是和外间距有关的。事实也确实如此,和的LayoutParams相比,MarginLayoutParams只是增加了对上下左右外间距的支持。实际上大部分的LayoutParams的实现类都是继承自MarginLayoutParams,因为基本所有的父容器都是支持子视图设置外间距的

  • 属性优先级问题MarginLayoutParams主要就是增加了上下左右4种外间距在构造方法中,先是获取了余量属性;如果该值不合法,就获取horizo​​ntalMargin;如果该值不合法,再去获取LEFTMARGIN和rightMargin属性(verticalMargin,TOPMARGIN和bottomMargin同理)。我们可以据此总结出这几种属性的优先级

margin> horizo​​ntalMargin和verticalMargin> leftMargin和RightMargin,topMargin和bottomMargin

  • 覆盖属性问题优先级更高的属性会覆盖掉优先级较低的属性。此外,还要注意一下这几种属性上的注释

重新分配新值后,调用{@link ViewGroup #setLayoutParams(LayoutParams)}

的LayoutParams与查看如何建立联系

  • 在XML中定义视图

  • 在Java的代码中直接生成查看对应的实例对象

addView

/ ** *重载方法1:添加一个子查看*如果这个子查看还没有LayoutParams,就为子查看设置当前ViewGroup默认的LayoutParams * / public void addViewView child){ addViewchild1); } / ** *重载方法2:在指定位置添加一个子查看*如果这个子查看还没有的LayoutParams,就为子查看设置当前的ViewGroup默认的的LayoutParams * @参数索引视图将在的ViewGroup中被添加的位置(-1代表添加到末尾)* / public void addViewView childint index){ ifchild == null)




   








    ){ throw new IllegalArgumentException“无法向ViewGroup添加空子视图”);   } LayoutParams params childgetLayoutParams(); ifparams == null){ params generateDefaultLayoutParams(); //生成当前ViewGroup默认的LayoutParams ifparams == null){ throw new IllegalArgumentException“generateDefaultLayoutParams()不能返回null”);       }   }
        

    
    
        
        
            


   addViewchildindexparams); 
} / ** *重载方法3:添加一个子查看*使用当前的ViewGroup默认的的LayoutParams,并以传入参数作为的LayoutParams的宽度和高度* / 公共空隙addView查看INT 宽度INT 高度){ final LayoutParams params =generateDefaultLayoutParams();  //生成当前ViewGroup默认的LayoutParams 参数width width ; 参数高度






    
    
    身高 ; addView孩子1PARAMS); } / ** *重载方法4:添加一个子查看,并使用传入的的LayoutParams * / @覆盖公共空隙addView查看的LayoutParams PARAMS){ addView1PARAMS); } / ** *重载方法4:在指定位置添加一个子视图,并使用传入的的LayoutParams * / 公共无效addView查看
   







   





childint indexLayoutParams params){ ifchild == null){ throw new IllegalArgumentException“无法向ViewGroup添加空子视图”);   } // addViewInner()将调用child.requestLayout()设置新的LayoutParams时//因此,我们之前自己打电话requestLayout(),让孩子的要求//将在我们的水平被阻止requestLayout(); 无效真实);addViewInner孩子,
    
        


   
   
   
   
   
   indexparamsfalse); 
} 私人无效addViewInner查看孩子INT 指数的LayoutParams PARAMS布尔preventRequestLayout){   ..... 如果mTransition != ){ mTransitionaddChildthischild);   } 如果checkLayoutParamsPARAMS)){


        

    
       


   //①检查传入的LayoutParams是否合法params generateLayoutParamsparams); //如果传入的的LayoutParams不合法,将进行转化操作   } 如果preventRequestLayout){ //②是否需要阻止重新执行布局流程mLayoutParams params ;//这不会引起子查看重新布局(onMeasure-> onLayout-> onDraw)   } else { childsetLayoutParamsparams); //这会引起子查看重新布局(onMeasure-> onLayout->的onDraw)   } 如果指数0){
        


   
        

       


    
       index mChildrenCount ; 
  } addInArray孩子索引); //告诉我们的孩子,如果preventRequestLayout){ 孩子assignParentthis);   } else {childmParent this ;   }   ..... }

   

   
   
       

        



*重载方法1:添加一个子查看*如果这个子查看还没有LayoutParams,就为子查看设置当前ViewGroup默认的LayoutParams * / public void addViewView child){ addViewchild1); } / ** *重载方法2:在指定位置添加一个子查看*如果这个子查看还没有的LayoutParams,就为子查看设置当前的ViewGroup默认的的LayoutParams * @参数索引视图将在的ViewGroup中被添加的位置(-1代表添加到末尾)* / public void addViewView childint index){ ifchild == null)




   








    ){ throw new IllegalArgumentException“无法向ViewGroup添加空子视图”);   } LayoutParams params childgetLayoutParams(); ifparams == null){ params generateDefaultLayoutParams(); //生成当前ViewGroup默认的LayoutParams ifparams == null){ throw new IllegalArgumentException“generateDefaultLayoutParams()不能返回null”);       }   }
        

    
    
        
        
            


   addViewchildindexparams); 
} / ** *重载方法3:添加一个子查看*使用当前的ViewGroup默认的的LayoutParams,并以传入参数作为的LayoutParams的宽度和高度* / 公共空隙addView查看INT 宽度INT 高度){ final LayoutParams params =generateDefaultLayoutParams();  //生成当前ViewGroup默认的LayoutParams 参数width width ; 参数高度






    
    
    身高 ; addView孩子1PARAMS); } / ** *重载方法4:添加一个子查看,并使用传入的的LayoutParams * / @覆盖公共空隙addView查看的LayoutParams PARAMS){ addView1PARAMS); } / ** *重载方法4:在指定位置添加一个子视图,并使用传入的的LayoutParams * / 公共无效addView查看
   







   





childint indexLayoutParams params){ ifchild == null){ throw new IllegalArgumentException“无法向ViewGroup添加空子视图”);   } // addViewInner()将调用child.requestLayout()设置新的LayoutParams时//因此,我们之前自己打电话requestLayout(),让孩子的要求//将在我们的水平被阻止requestLayout(); 无效真实);addViewInner孩子,
    
        


   
   
   
   
   
   indexparamsfalse); 
} 私人无效addViewInner查看孩子INT 指数的LayoutParams PARAMS布尔preventRequestLayout){   ..... 如果mTransition != ){ mTransitionaddChildthischild);   } 如果checkLayoutParamsPARAMS)){


        

    
       


   //①检查传入的LayoutParams是否合法params generateLayoutParamsparams); //如果传入的的LayoutParams不合法,将进行转化操作   } 如果preventRequestLayout){ //②是否需要阻止重新执行布局流程mLayoutParams params ;//这不会引起子查看重新布局(onMeasure-> onLayout-> onDraw)   } else { childsetLayoutParamsparams); //这会引起子查看重新布局(onMeasure-> onLayout->的onDraw)   } 如果指数0){
        


   
        

       


    
       index mChildrenCount ; 
  } addInArray孩子索引); //告诉我们的孩子,如果preventRequestLayout){ 孩子assignParentthis);   } else {childmParent this ;   }   ..... }

   

   
   
       

        



自定义的LayoutParams

  1. 创建自定义属性

resources declare styleable name “xxxViewGroup_Layout” <! - 自定义的属性- > attr name =“layout_simple_attr” format “integer” /> <! - 使用系统预置的属性- > attr name “android:layout_gravity”/> declare styleable resources >
    
        
        
        
        
   
resources declare styleable name “xxxViewGroup_Layout” <! - 自定义的属性- > attr name =“layout_simple_attr” format “integer” /> <! - 使用系统预置的属性- > attr name “android:layout_gravity”/> declare styleable resources >
    
        
        
        
        
   
  1. 继承MarginLayout

public static class LayoutParams 扩展了 ViewGroupMarginLayoutParams { public int simpleAttr ; 公共INT 重力 ; 公共的LayoutParams上下文ÇAttributeSet中ATTRS){ 超级ÇATTRS); //解析布局属性TypedArray typedArray cobtainStyledAttributesATTRS- [R 设置样式。
    
    

    
       
       
        SimpleViewGroup_Layout); simpleAttr typedArraygetIntegerř设置样式SimpleViewGroup_Layout_layout_simple_attr0); gravity typedArraygetIntegerř设置样式SimpleViewGroup_Layout_android_layout_gravity1); typedArrayrecycle(); //释放资源   } 公共的LayoutParamsINT 宽度,
        
       

       


    int height){ superwidthheight);   } 公共的LayoutParamsMarginLayoutParams ){ 超级);   } 公共的LayoutParamsViewGroup中的LayoutParams ){ 超级);   } }
       


    
       


    
       

static class LayoutParams 扩展了 ViewGroupMarginLayoutParams { public int simpleAttr ; 公共INT 重力 ; 公共的LayoutParams上下文ÇAttributeSet中ATTRS){ 超级ÇATTRS); //解析布局属性TypedArray typedArray cobtainStyledAttributesATTRS- [R 设置样式。
    
    

    
       
       
        SimpleViewGroup_Layout); simpleAttr typedArraygetIntegerř设置样式SimpleViewGroup_Layout_layout_simple_attr0); gravity typedArraygetIntegerř设置样式SimpleViewGroup_Layout_android_layout_gravity1); typedArrayrecycle(); //释放资源   } 公共的LayoutParamsINT 宽度,
        
       

       


    int height){ superwidthheight);   } 公共的LayoutParamsMarginLayoutParams ){ 超级);   } 公共的LayoutParamsViewGroup中的LayoutParams ){ 超级);   } }
       


    
       


    
       

  1. 重写的ViewGroup中几个与的LayoutParams相关的方法

//检查的LayoutParams是否合法
@覆盖
保护 布尔 checkLayoutParamsViewGroup中的LayoutParams p){ 返回的instanceof SimpleViewGroupLayoutParams ; } //生成默认的的LayoutParams @覆盖保护的ViewGroupLayoutParamsgenerateDefaultLayoutParams(){ 返回新的SimpleViewGroup的LayoutParams的LayoutParamsMATCH_PARENT的LayoutParamsWRAP_CONTENT
    





    ); 
} //对传入的的LayoutParams进行转化@覆盖保护的ViewGroup的LayoutParams generateLayoutParamsViewGroup中的LayoutParams p){ 返回SimpleViewGroupLayoutParamsp); } //对传入的的LayoutParams进行转化@覆盖公众的ViewGroupLayoutParams generateLayoutParamsAttributeSet attrs){ 返回新的SimpleViewGroupLayoutParams(




    





    getContext(),attrs); 
}
@覆盖
保护 布尔 checkLayoutParamsViewGroup中的LayoutParams p){ 返回的instanceof SimpleViewGroupLayoutParams ; } //生成默认的的LayoutParams @覆盖保护的ViewGroupLayoutParamsgenerateDefaultLayoutParams(){ 返回新的SimpleViewGroup的LayoutParams的LayoutParamsMATCH_PARENT的LayoutParamsWRAP_CONTENT
    





    ); 
} //对传入的的LayoutParams进行转化@覆盖保护的ViewGroup的LayoutParams generateLayoutParamsViewGroup中的LayoutParams p){ 返回SimpleViewGroupLayoutParamsp); } //对传入的的LayoutParams进行转化@覆盖公众的ViewGroupLayoutParams generateLayoutParamsAttributeSet attrs){ 返回新的SimpleViewGroupLayoutParams(




    





    getContext(),attrs); 
}

的LayoutParams常见的子类

在为视图设置的LayoutParams的时候需要根据它的父容器选择对应的的LayoutParams,否则结果可能与预期不一致,这里简单罗列一些常见的的LayoutParams子类:

  • ViewGroup.MarginLayoutParams

  • FrameLayout.LayoutParams

  • LinearLayout.LayoutParams

  • RelativeLayout.LayoutParams

  • RecyclerView.LayoutParams

  • GridLayoutManager.LayoutParams

  • StaggeredGridLayoutManager.LayoutParams

  • ViewPager.LayoutParams

  • WindowManager.LayoutParams

MeasureSpec

定义

测量规格,封装了父容器对查看的布局上的限制,内部提供了宽高的信息(SpecMode,SpecSize),SpecSize是指在某种SpecMode下的参考尺寸,其中SpecMode有如下三种:

  • UNSPECIFIED 父控件不对你有任何限制,你想要多大给你多大,想上天就上天。这种情况一般用于系统内部,表示一种测量状态。(这个模式主要用于系统内部多次测量的情形,并不是真的说你想要多大最后就真有多大)

  • EXACTLY 父控件已经知道你所需的精确大小,你的最终大小应该就是这么大。

  • AT_MOST 你的大小不能大于父控件给你指定的大小,但具体是多少,得看你自己的实现。

MeasureSpecs的意义

通过将SpecMode和SpecSize打包成一个int值可以避免过多的对象内存分配,为了方便操作,其提供了打包/解包方法

MeasureSpec值的确定

MeasureSpec值到底是如何计算得来的呢? 子视图的MeasureSpec值是根据子查看的布局参数(的LayoutParams)和父容器的MeasureSpec值计算得来的,具体计算逻辑封装在getChildMeasureSpec()里

 / ** *目标是将父控件的测量规格和子视图的布局参数的LayoutParams相结合,得到一个*最可能符合条件的子视图的测量规格。   * @参数规格父控件的测量规格* @参数padding父控件里已经占用的大小* @param childDimension子视图布局LayoutParams里的尺寸* @return子视图的测量规格* / public static int getChildMeasureSpecint specint paddingintchildDimension){ int specMode MeasureSpecgetModespec); //父控件的测量模式int specSize MeasureSpec
    
    
    

    
    
    
    
    
    
        
        。getSizespec); //父控件的测量大小INT 大小数学最大值0specSize 填充); INT resultSize 0 ; INTresultMode 0 ; 开关specMode){ //当父控件的测量模式是精确模式,也就是有精确的尺寸了壳体MeasureSpecEXACTLY//如果孩子的布局参数有固定值,比如“layout_width”= “100dp” //那么显然孩子的测量规格也可以确定下来了,测量大小就是100dp,测量模式也是EXACTLY 如果

        

        
        

       
       
        
           
           
           (childDimension > = 0){ resultSize childDimension ; resultMode MeasureSpec确切地说 ;           } //如果孩子的布局参数是“match_parent”,也就是想要占满父控件//而此时父控件是精确模式,也就是能确定自己的尺寸了,那孩子也能确定自己大小了否则如果childDimension == 的LayoutParamsMATCH_PARENT){ resultSize 大小 ; resultMode =MeasureSpec确切地说 ;           } //如果child的布局参数是“wrap_content”,也就是想要根据自己的逻辑决定自己大小,
                
                


           
           
            
                
                

           
           //比如TextView的根据设置的字符串大小来决定自己的大小//那就自己决定呗,不过你的大小肯定不能大于父控件的大小嘛//所以测量模式就是AT_MOST,测量大小就是父控件的大小否则如果childDimension == 的LayoutParamsWRAP_CONTENT){ resultSize 大小 ; resultMode MeasureSpecAT_MOST ;           } 打破 ; //当父控件的测量模式是最大模式,也就是说父控件自己还不知道自己的尺寸,但是大小不能超过尺寸的情况下MeasureSpecAT_MOST//同样的,既然孩子能确定自己大小,尽管父控件自己还不知道自己大小,也优先满足孩子的需求ifchildDimension > = 0
           
           
            
                
                

           

       
        
           
            ){ resultSize childDimension ; resultMode MeasureSpec确切地说 ;           } // child想要和父控件一样大,但父控件自己也不确定自己大小,所以孩子也无法确定自己大小//但同样的,孩子的尺寸上限也是父控件的尺寸上限尺寸ififchildDimension == 的LayoutParamsMATCH_PARENT){ resultSize 大小 ; resultMode MeasureSpecAT_MOST ;           } //孩子想要根据自己逻辑决定大小,那就自己决定呗否则如果childDimension ==
                
                

           
           
            
                
                

           
            LayoutParamsWRAP_CONTENT){ resultSize size ; resultMode MeasureSpecAT_MOST ;           } 打破 ; //家长要求看,我们希望有多大是情况MeasureSpec好评ifchildDimension > = 0){ //孩子想要一个特定的大小...让他拥有它resultSize childDimension ; resultMode MeasureSpec确切地说 ;           }
                
                

           

       
        
            
               
                
                
否则 ,如果childDimension == 的LayoutParamsMATCH_PARENT){ //儿童希望成为我们的大小...找出它应该有多大//是resultSize 0 ; resultMode MeasureSpec未知 ;           } 否则如果childDimension == 的LayoutParamsWRAP_CONTENT){ //孩子想确定自己的大小....找出如何应该//大是resultSize 0 ; resultMode =
               
               
                
                

               
               
                
                MeasureSpec未知 ; 
          } 打破 ;       } 返回MeasureSpecmakeMeasureSpecresultSizeresultMode);   }
           

        
/ ** *目标是将父控件的测量规格和子视图的布局参数的LayoutParams相结合,得到一个*最可能符合条件的子视图的测量规格。   * @参数规格父控件的测量规格* @参数padding父控件里已经占用的大小* @param childDimension子视图布局LayoutParams里的尺寸* @return子视图的测量规格* / public static int getChildMeasureSpecint specint paddingintchildDimension){ int specMode MeasureSpecgetModespec); //父控件的测量模式int specSize MeasureSpec
    
    
    

    
    
    
    
    
    
        
        。getSizespec); //父控件的测量大小INT 大小数学最大值0specSize 填充); INT resultSize 0 ; INTresultMode 0 ; 开关specMode){ //当父控件的测量模式是精确模式,也就是有精确的尺寸了壳体MeasureSpecEXACTLY//如果孩子的布局参数有固定值,比如“layout_width”= “100dp” //那么显然孩子的测量规格也可以确定下来了,测量大小就是100dp,测量模式也是EXACTLY 如果

        

        
        

       
       
        
           
           
           (childDimension > = 0){ resultSize childDimension ; resultMode MeasureSpec确切地说 ;           } //如果孩子的布局参数是“match_parent”,也就是想要占满父控件//而此时父控件是精确模式,也就是能确定自己的尺寸了,那孩子也能确定自己大小了否则如果childDimension == 的LayoutParamsMATCH_PARENT){ resultSize 大小 ; resultMode =MeasureSpec确切地说 ;           } //如果child的布局参数是“wrap_content”,也就是想要根据自己的逻辑决定自己大小,
                
                


           
           
            
                
                

           
           //比如TextView的根据设置的字符串大小来决定自己的大小//那就自己决定呗,不过你的大小肯定不能大于父控件的大小嘛//所以测量模式就是AT_MOST,测量大小就是父控件的大小否则如果childDimension == 的LayoutParamsWRAP_CONTENT){ resultSize 大小 ; resultMode MeasureSpecAT_MOST ;           } 打破 ; //当父控件的测量模式是最大模式,也就是说父控件自己还不知道自己的尺寸,但是大小不能超过尺寸的情况下MeasureSpecAT_MOST//同样的,既然孩子能确定自己大小,尽管父控件自己还不知道自己大小,也优先满足孩子的需求ifchildDimension > = 0
           
           
            
                
                

           

       
        
           
            ){ resultSize childDimension ; resultMode MeasureSpec确切地说 ;           } // child想要和父控件一样大,但父控件自己也不确定自己大小,所以孩子也无法确定自己大小//但同样的,孩子的尺寸上限也是父控件的尺寸上限尺寸ififchildDimension == 的LayoutParamsMATCH_PARENT){ resultSize 大小 ; resultMode MeasureSpecAT_MOST ;           } //孩子想要根据自己逻辑决定大小,那就自己决定呗否则如果childDimension ==
                
                

           
           
            
                
                

           
            LayoutParamsWRAP_CONTENT){ resultSize size ; resultMode MeasureSpecAT_MOST ;           } 打破 ; //家长要求看,我们希望有多大是情况MeasureSpec好评ifchildDimension > = 0){ //孩子想要一个特定的大小...让他拥有它resultSize childDimension ; resultMode MeasureSpec确切地说 ;           }
                
                

           

       
        
            
               
                
                
否则 ,如果childDimension == 的LayoutParamsMATCH_PARENT){ //儿童希望成为我们的大小...找出它应该有多大//是resultSize 0 ; resultMode MeasureSpec未知 ;           } 否则如果childDimension == 的LayoutParamsWRAP_CONTENT){ //孩子想确定自己的大小....找出如何应该//大是resultSize 0 ; resultMode =
               
               
                
                

               
               
                
                MeasureSpec未知 ; 
          } 打破 ;       } 返回MeasureSpecmakeMeasureSpecresultSizeresultMode);   }
           

        

针对上表,这里再做一下具体的说明

  • 对于应用层View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定

  • 对于不同的父容器和查看本身不同的LayoutParams,查看就可以有多种MeasureSpec.1。当前 视图采用固定宽高的时候,不管父容器的MeasureSpec是什么,查看的MeasureSpec都是精确模式并且其大小遵循Layoutparams中的大小; 2.当观的宽宽是match_parent时,这个时候如果父容器的模式是精准模式,那么观也是精准模式并且其大小是父容器的剩余空间,如果父容器是最大模式,那么查看也是最大模式并且其大小不会超过父容器的剩余空间; 3.当查的宽高是wrap_content时,不管父容器的模式是精准还是最大化,查看的模式总是最大化并且大小不能超过父容器的剩余空间.4。 未指明的模式,这个模式主要用于系统内部多次测量的情况下,一般来说,我们不需要关注此模式(这里注意自定义查看放到ScrollView的情况需要处理)。

 

你可能感兴趣的:(Android的自定义视图)