自定义查看的最基本的三个方法分别是:onMeasure(),onLayout(),onDraw(); 查看在活动中显示出来,要经历测量,布局和绘制三个步骤,分别对应三个动作:测量,布局和平局。
测量:onMeasure()决定查看的大小;
布局:onLayout()决定查看在一个ViewGroup中的位置;
绘制:的onDraw()决定绘制这个视图。
自定义查看:只需要重写onMeasure()和onDraw()
自定义ViewGroup:则只需要重写onMeasure()和onLayout()
视图查看主要分为两类
类别 | 解释 | 特点 |
---|---|---|
单一视图 | 即一个视图,如TextView的 | 不包含子视图 |
视图组 | 即多个视图组成的ViewGroup中,如的LinearLayout | 包含子视图 |
查看类是Android的中各种组件的基类,如视图是一个ViewGroup基类
查看表现为显示在屏幕上的各种视图
安卓中的UI组件都由查看,ViewGroup中组成。
查看的构造函数:共有4个
//如果查看是在Java代码里面new的,则调用第一个构造函数public CarsonView(Context context){super(context); } //如果视图是在.XML里声明的,则调用第二个构造函数//自定义属性是从AttributeSet中参数传进来的公共CarsonView(语境上下文,AttributeSet中ATTRS){ 超级(上下文,ATTRS); } //不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView(上下文语境,
AttributeSet attrs,int defStyleAttr){ super(context,attrs,defStyleAttr); } // API21之后才使用//不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView(上下文语境,AttributeSet中的ATTRS,INT defStyleAttr,INT defStyleRes) {super(context,attrs,defStyleAttr,defStyleRes);
}
public CarsonView(Context context){super(context); } //如果视图是在.XML里声明的,则调用第二个构造函数//自定义属性是从AttributeSet中参数传进来的公共CarsonView(语境上下文,AttributeSet中ATTRS){ 超级(上下文,ATTRS); } //不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView(上下文语境,
AttributeSet attrs,int defStyleAttr){ super(context,attrs,defStyleAttr); } // API21之后才使用//不会自动调用//一般是在第二个构造函数里主动调用//如查看有风格属性时公开CarsonView(上下文语境,AttributeSet中的ATTRS,INT defStyleAttr,INT defStyleRes) {super(context,attrs,defStyleAttr,defStyleRes);
}
系统自带的浏览可以在XML中配置属性,对于写的好的自定义视图同样可以在XML中配置属性,为了使自定义的视图的属性可以在XML中配置,需要以下4个步骤:
通过为自定义视图添加属性
在XML中为相应的属性声明属性值
在运行时(一般为构造函数)获取属性值
将获取到的属性值应用到景观
PhoneWindow是Android的系统中最基本的窗口系统,继承自视窗类,负责管理界面显示以及事件响应。它是活动与景观系统交互的接口
DecorView是PhoneWindow中的起始节点观,继承于视图类,作为整个视图容器来使用。用于设置窗口属性。它本质上是一个的FrameLayout
的ViewRoot在Activtiy启动时创建,负责管理,布局,渲染窗口UI等等
对于多视图的视图,结构是树形结构:最顶层是一个ViewGroup的ViewGroup以及下可能有多个ViewGroup中或查看,如下图:
一定要记住:无论是测量过程,布局过程还是绘制过程,永远都是从搜索树的根节点开始测量或计算(即从树的顶端开始),一层一层,一个分支一个分支地进行(即树形递归),最终计算整个景观树中各个视图,最终确定整个景观树的相关属性。
安卓的坐标系定义为:
屏幕的左上角为坐标原点
向右为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与之关联,这种关联关系是由窗口管理去进行管理的;
系统为什么要有措施过程?
测量过程都干了点什么事?
对于自适应的尺寸机制,如何合理的测量一颗景观树?
那么ViewGroup中是如何向子查看传递限制信息的?
滚动型嵌套的ListView问题?
系统为什么要有布局过程?
布局过程都干了点什么事?
系统为什么要有画过程?
绘制过程都干了点什么事?
ayoutParams翻译过来就是布局参数,子视图通过的LayoutParams告诉父容器(ViewGroup中)应该如何放置自己。从这个定义中也可以看出来的LayoutParams与ViewGroup中是息息相关的,因此脱离的ViewGroup谈的LayoutParams是没有意义的。
事实上,每个的ViewGroup的子类都有自己对应的的LayoutParams类,典型的如LinearLayout.LayoutParams和FrameLayout.LayoutParams等,可以看出来的LayoutParams都是对应的ViewGroup子类的内部类
MarginLayoutParams是和外间距有关的。事实也确实如此,和的LayoutParams相比,MarginLayoutParams只是增加了对上下左右外间距的支持。实际上大部分的LayoutParams的实现类都是继承自MarginLayoutParams,因为基本所有的父容器都是支持子视图设置外间距的
属性优先级问题MarginLayoutParams主要就是增加了上下左右4种外间距在构造方法中,先是获取了余量属性;如果该值不合法,就获取horizontalMargin;如果该值不合法,再去获取LEFTMARGIN和rightMargin属性(verticalMargin,TOPMARGIN和bottomMargin同理)。我们可以据此总结出这几种属性的优先级
margin> horizontalMargin和verticalMargin> leftMargin和RightMargin,topMargin和bottomMargin
覆盖属性问题优先级更高的属性会覆盖掉优先级较低的属性。此外,还要注意一下这几种属性上的注释
重新分配新值后,调用{@link ViewGroup #setLayoutParams(LayoutParams)}
在XML中定义视图
在Java的代码中直接生成查看对应的实例对象
/ ** *重载方法1:添加一个子查看*如果这个子查看还没有LayoutParams,就为子查看设置当前ViewGroup默认的LayoutParams * / public void addView(View child){ addView(child,- 1); } / ** *重载方法2:在指定位置添加一个子查看*如果这个子查看还没有的LayoutParams,就为子查看设置当前的ViewGroup默认的的LayoutParams * @参数索引视图将在的ViewGroup中被添加的位置(-1代表添加到末尾)* / public void addView(View child,int index){ if(child == null)
){ throw new IllegalArgumentException(“无法向ViewGroup添加空子视图”); } LayoutParams params = child。getLayoutParams(); if(params == null){ params = generateDefaultLayoutParams(); //生成当前ViewGroup默认的LayoutParams if(params == null){ throw new IllegalArgumentException(“generateDefaultLayoutParams()不能返回null”); } }
addView(child,index,params);
} / ** *重载方法3:添加一个子查看*使用当前的ViewGroup默认的的LayoutParams,并以传入参数作为的LayoutParams的宽度和高度* / 公共空隙addView(查看子,INT 宽度,INT 高度){ final LayoutParams params =generateDefaultLayoutParams(); //生成当前ViewGroup默认的LayoutParams 参数。width = width ; 参数。高度
= 身高 ; addView(孩子,- 1,PARAMS); } / ** *重载方法4:添加一个子查看,并使用传入的的LayoutParams * / @覆盖公共空隙addView(查看子,的LayoutParams PARAMS){ addView(子,- 1,PARAMS); } / ** *重载方法4:在指定位置添加一个子视图,并使用传入的的LayoutParams * / 公共无效addView(查看
child,int index,LayoutParams params){ if(child == null){ throw new IllegalArgumentException(“无法向ViewGroup添加空子视图”); } // addViewInner()将调用child.requestLayout()设置新的LayoutParams时//因此,我们之前自己打电话requestLayout(),让孩子的要求//将在我们的水平被阻止requestLayout(); 无效(真实);addViewInner(孩子,
index,params,false);
} 私人无效addViewInner(查看孩子,INT 指数,的LayoutParams PARAMS,布尔preventRequestLayout){ ..... 如果(mTransition != 空){ mTransition。addChild(this,child); } 如果(!checkLayoutParams(PARAMS)){
//①检查传入的LayoutParams是否合法params = generateLayoutParams(params); //如果传入的的LayoutParams不合法,将进行转化操作 } 如果(preventRequestLayout){ //②是否需要阻止重新执行布局流程子。mLayoutParams = params ;//这不会引起子查看重新布局(onMeasure-> onLayout-> onDraw) } else { child。setLayoutParams(params); //这会引起子查看重新布局(onMeasure-> onLayout->的onDraw) } 如果(指数< 0){
index = mChildrenCount ;
} addInArray(孩子,索引); //告诉我们的孩子,如果(preventRequestLayout){ 孩子。assignParent(this); } else {child。mParent = this ; } ..... }
*重载方法1:添加一个子查看*如果这个子查看还没有LayoutParams,就为子查看设置当前ViewGroup默认的LayoutParams * / public void addView(View child){ addView(child,- 1); } / ** *重载方法2:在指定位置添加一个子查看*如果这个子查看还没有的LayoutParams,就为子查看设置当前的ViewGroup默认的的LayoutParams * @参数索引视图将在的ViewGroup中被添加的位置(-1代表添加到末尾)* / public void addView(View child,int index){ if(child == null)
){ throw new IllegalArgumentException(“无法向ViewGroup添加空子视图”); } LayoutParams params = child。getLayoutParams(); if(params == null){ params = generateDefaultLayoutParams(); //生成当前ViewGroup默认的LayoutParams if(params == null){ throw new IllegalArgumentException(“generateDefaultLayoutParams()不能返回null”); } }
addView(child,index,params);
} / ** *重载方法3:添加一个子查看*使用当前的ViewGroup默认的的LayoutParams,并以传入参数作为的LayoutParams的宽度和高度* / 公共空隙addView(查看子,INT 宽度,INT 高度){ final LayoutParams params =generateDefaultLayoutParams(); //生成当前ViewGroup默认的LayoutParams 参数。width = width ; 参数。高度
= 身高 ; addView(孩子,- 1,PARAMS); } / ** *重载方法4:添加一个子查看,并使用传入的的LayoutParams * / @覆盖公共空隙addView(查看子,的LayoutParams PARAMS){ addView(子,- 1,PARAMS); } / ** *重载方法4:在指定位置添加一个子视图,并使用传入的的LayoutParams * / 公共无效addView(查看
child,int index,LayoutParams params){ if(child == null){ throw new IllegalArgumentException(“无法向ViewGroup添加空子视图”); } // addViewInner()将调用child.requestLayout()设置新的LayoutParams时//因此,我们之前自己打电话requestLayout(),让孩子的要求//将在我们的水平被阻止requestLayout(); 无效(真实);addViewInner(孩子,
index,params,false);
} 私人无效addViewInner(查看孩子,INT 指数,的LayoutParams PARAMS,布尔preventRequestLayout){ ..... 如果(mTransition != 空){ mTransition。addChild(this,child); } 如果(!checkLayoutParams(PARAMS)){
//①检查传入的LayoutParams是否合法params = generateLayoutParams(params); //如果传入的的LayoutParams不合法,将进行转化操作 } 如果(preventRequestLayout){ //②是否需要阻止重新执行布局流程子。mLayoutParams = params ;//这不会引起子查看重新布局(onMeasure-> onLayout-> onDraw) } else { child。setLayoutParams(params); //这会引起子查看重新布局(onMeasure-> onLayout->的onDraw) } 如果(指数< 0){
index = mChildrenCount ;
} addInArray(孩子,索引); //告诉我们的孩子,如果(preventRequestLayout){ 孩子。assignParent(this); } else {child。mParent = this ; } ..... }
创建自定义属性
< 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 >
继承MarginLayout
public static class LayoutParams 扩展了 ViewGroup。MarginLayoutParams { public int simpleAttr ; 公共INT 重力 ; 公共的LayoutParams(上下文Ç,AttributeSet中ATTRS){ 超级(Ç,ATTRS); //解析布局属性TypedArray typedArray = c。obtainStyledAttributes(ATTRS,- [R 。设置样式。
SimpleViewGroup_Layout); simpleAttr = typedArray。getInteger(ř。设置样式。SimpleViewGroup_Layout_layout_simple_attr,0); gravity = typedArray。getInteger(ř。设置样式。SimpleViewGroup_Layout_android_layout_gravity,- 1); typedArray。recycle(); //释放资源 } 公共的LayoutParams(INT 宽度,
int height){ super(width,height); } 公共的LayoutParams(MarginLayoutParams 源){ 超级(源); } 公共的LayoutParams(ViewGroup中。的LayoutParams 源){ 超级(源); } }
static class LayoutParams 扩展了 ViewGroup。MarginLayoutParams { public int simpleAttr ; 公共INT 重力 ; 公共的LayoutParams(上下文Ç,AttributeSet中ATTRS){ 超级(Ç,ATTRS); //解析布局属性TypedArray typedArray = c。obtainStyledAttributes(ATTRS,- [R 。设置样式。
SimpleViewGroup_Layout); simpleAttr = typedArray。getInteger(ř。设置样式。SimpleViewGroup_Layout_layout_simple_attr,0); gravity = typedArray。getInteger(ř。设置样式。SimpleViewGroup_Layout_android_layout_gravity,- 1); typedArray。recycle(); //释放资源 } 公共的LayoutParams(INT 宽度,
int height){ super(width,height); } 公共的LayoutParams(MarginLayoutParams 源){ 超级(源); } 公共的LayoutParams(ViewGroup中。的LayoutParams 源){ 超级(源); } }
重写的ViewGroup中几个与的LayoutParams相关的方法
//检查的LayoutParams是否合法
@覆盖
保护 布尔 checkLayoutParams(ViewGroup中。的LayoutParams p){ 返回p 的instanceof SimpleViewGroup。LayoutParams ; } //生成默认的的LayoutParams @覆盖保护的ViewGroup。LayoutParamsgenerateDefaultLayoutParams(){ 返回新的SimpleViewGroup。的LayoutParams(的LayoutParams。MATCH_PARENT,的LayoutParams。WRAP_CONTENT
);
} //对传入的的LayoutParams进行转化@覆盖保护的ViewGroup。的LayoutParams generateLayoutParams(ViewGroup中。的LayoutParams p){ 返回新SimpleViewGroup。LayoutParams(p); } //对传入的的LayoutParams进行转化@覆盖公众的ViewGroup。LayoutParams generateLayoutParams(AttributeSet attrs){ 返回新的SimpleViewGroup。LayoutParams(
getContext(),attrs);
}
@覆盖
保护 布尔 checkLayoutParams(ViewGroup中。的LayoutParams p){ 返回p 的instanceof SimpleViewGroup。LayoutParams ; } //生成默认的的LayoutParams @覆盖保护的ViewGroup。LayoutParamsgenerateDefaultLayoutParams(){ 返回新的SimpleViewGroup。的LayoutParams(的LayoutParams。MATCH_PARENT,的LayoutParams。WRAP_CONTENT
);
} //对传入的的LayoutParams进行转化@覆盖保护的ViewGroup。的LayoutParams generateLayoutParams(ViewGroup中。的LayoutParams p){ 返回新SimpleViewGroup。LayoutParams(p); } //对传入的的LayoutParams进行转化@覆盖公众的ViewGroup。LayoutParams generateLayoutParams(AttributeSet attrs){ 返回新的SimpleViewGroup。LayoutParams(
getContext(),attrs);
}
在为视图设置的LayoutParams的时候需要根据它的父容器选择对应的的LayoutParams,否则结果可能与预期不一致,这里简单罗列一些常见的的LayoutParams子类:
ViewGroup.MarginLayoutParams
FrameLayout.LayoutParams
LinearLayout.LayoutParams
RelativeLayout.LayoutParams
RecyclerView.LayoutParams
GridLayoutManager.LayoutParams
StaggeredGridLayoutManager.LayoutParams
ViewPager.LayoutParams
WindowManager.LayoutParams
测量规格,封装了父容器对查看的布局上的限制,内部提供了宽高的信息(SpecMode,SpecSize),SpecSize是指在某种SpecMode下的参考尺寸,其中SpecMode有如下三种:
UNSPECIFIED 父控件不对你有任何限制,你想要多大给你多大,想上天就上天。这种情况一般用于系统内部,表示一种测量状态。(这个模式主要用于系统内部多次测量的情形,并不是真的说你想要多大最后就真有多大)
EXACTLY 父控件已经知道你所需的精确大小,你的最终大小应该就是这么大。
AT_MOST 你的大小不能大于父控件给你指定的大小,但具体是多少,得看你自己的实现。
通过将SpecMode和SpecSize打包成一个int值可以避免过多的对象内存分配,为了方便操作,其提供了打包/解包方法
MeasureSpec值到底是如何计算得来的呢? 子视图的MeasureSpec值是根据子查看的布局参数(的LayoutParams)和父容器的MeasureSpec值计算得来的,具体计算逻辑封装在getChildMeasureSpec()里
/ ** * *目标是将父控件的测量规格和子视图的布局参数的LayoutParams相结合,得到一个*最可能符合条件的子视图的测量规格。 * @参数规格父控件的测量规格* @参数padding父控件里已经占用的大小* @param childDimension子视图布局LayoutParams里的尺寸* @return子视图的测量规格* / public static int getChildMeasureSpec(int spec,int padding,intchildDimension){ int specMode = MeasureSpec。getMode(spec); //父控件的测量模式int specSize = MeasureSpec
。getSize(spec); //父控件的测量大小INT 大小= 数学。最大值(0,specSize - 填充); INT resultSize = 0 ; INTresultMode = 0 ; 开关(specMode){ //当父控件的测量模式是精确模式,也就是有精确的尺寸了壳体MeasureSpec。EXACTLY://如果孩子的布局参数有固定值,比如“layout_width”= “100dp” //那么显然孩子的测量规格也可以确定下来了,测量大小就是100dp,测量模式也是EXACTLY 如果
(childDimension > = 0){ resultSize = childDimension ; resultMode = MeasureSpec。确切地说 ; } //如果孩子的布局参数是“match_parent”,也就是想要占满父控件//而此时父控件是精确模式,也就是能确定自己的尺寸了,那孩子也能确定自己大小了否则如果(childDimension == 的LayoutParams。MATCH_PARENT){ resultSize = 大小 ; resultMode =MeasureSpec。确切地说 ; } //如果child的布局参数是“wrap_content”,也就是想要根据自己的逻辑决定自己大小,
//比如TextView的根据设置的字符串大小来决定自己的大小//那就自己决定呗,不过你的大小肯定不能大于父控件的大小嘛//所以测量模式就是AT_MOST,测量大小就是父控件的大小否则如果(childDimension == 的LayoutParams。WRAP_CONTENT){ resultSize = 大小 ; resultMode = MeasureSpec。AT_MOST ; } 打破 ; //当父控件的测量模式是最大模式,也就是说父控件自己还不知道自己的尺寸,但是大小不能超过尺寸的情况下MeasureSpec。AT_MOST://同样的,既然孩子能确定自己大小,尽管父控件自己还不知道自己大小,也优先满足孩子的需求if(childDimension > = 0
){ resultSize = childDimension ; resultMode = MeasureSpec。确切地说 ; } // child想要和父控件一样大,但父控件自己也不确定自己大小,所以孩子也无法确定自己大小//但同样的,孩子的尺寸上限也是父控件的尺寸上限尺寸ifif(childDimension == 的LayoutParams。MATCH_PARENT){ resultSize = 大小 ; resultMode = MeasureSpec。AT_MOST ; } //孩子想要根据自己逻辑决定大小,那就自己决定呗否则如果(childDimension ==
LayoutParams。WRAP_CONTENT){ resultSize = size ; resultMode = MeasureSpec。AT_MOST ; } 打破 ; //家长要求看,我们希望有多大是情况MeasureSpec。好评:if(childDimension > = 0){ //孩子想要一个特定的大小...让他拥有它resultSize = childDimension ; resultMode = MeasureSpec。确切地说 ; }
否则 ,如果(childDimension == 的LayoutParams。MATCH_PARENT){ //儿童希望成为我们的大小...找出它应该有多大//是resultSize = 0 ; resultMode = MeasureSpec。未知 ; } 否则如果(childDimension == 的LayoutParams。WRAP_CONTENT){ //孩子想确定自己的大小....找出如何应该//大是resultSize = 0 ; resultMode =
MeasureSpec。未知 ;
} 打破 ; } 返回MeasureSpec。makeMeasureSpec(resultSize,resultMode); }
/ ** * *目标是将父控件的测量规格和子视图的布局参数的LayoutParams相结合,得到一个*最可能符合条件的子视图的测量规格。 * @参数规格父控件的测量规格* @参数padding父控件里已经占用的大小* @param childDimension子视图布局LayoutParams里的尺寸* @return子视图的测量规格* / public static int getChildMeasureSpec(int spec,int padding,intchildDimension){ int specMode = MeasureSpec。getMode(spec); //父控件的测量模式int specSize = MeasureSpec
。getSize(spec); //父控件的测量大小INT 大小= 数学。最大值(0,specSize - 填充); INT resultSize = 0 ; INTresultMode = 0 ; 开关(specMode){ //当父控件的测量模式是精确模式,也就是有精确的尺寸了壳体MeasureSpec。EXACTLY://如果孩子的布局参数有固定值,比如“layout_width”= “100dp” //那么显然孩子的测量规格也可以确定下来了,测量大小就是100dp,测量模式也是EXACTLY 如果
(childDimension > = 0){ resultSize = childDimension ; resultMode = MeasureSpec。确切地说 ; } //如果孩子的布局参数是“match_parent”,也就是想要占满父控件//而此时父控件是精确模式,也就是能确定自己的尺寸了,那孩子也能确定自己大小了否则如果(childDimension == 的LayoutParams。MATCH_PARENT){ resultSize = 大小 ; resultMode =MeasureSpec。确切地说 ; } //如果child的布局参数是“wrap_content”,也就是想要根据自己的逻辑决定自己大小,
//比如TextView的根据设置的字符串大小来决定自己的大小//那就自己决定呗,不过你的大小肯定不能大于父控件的大小嘛//所以测量模式就是AT_MOST,测量大小就是父控件的大小否则如果(childDimension == 的LayoutParams。WRAP_CONTENT){ resultSize = 大小 ; resultMode = MeasureSpec。AT_MOST ; } 打破 ; //当父控件的测量模式是最大模式,也就是说父控件自己还不知道自己的尺寸,但是大小不能超过尺寸的情况下MeasureSpec。AT_MOST://同样的,既然孩子能确定自己大小,尽管父控件自己还不知道自己大小,也优先满足孩子的需求if(childDimension > = 0
){ resultSize = childDimension ; resultMode = MeasureSpec。确切地说 ; } // child想要和父控件一样大,但父控件自己也不确定自己大小,所以孩子也无法确定自己大小//但同样的,孩子的尺寸上限也是父控件的尺寸上限尺寸ifif(childDimension == 的LayoutParams。MATCH_PARENT){ resultSize = 大小 ; resultMode = MeasureSpec。AT_MOST ; } //孩子想要根据自己逻辑决定大小,那就自己决定呗否则如果(childDimension ==
LayoutParams。WRAP_CONTENT){ resultSize = size ; resultMode = MeasureSpec。AT_MOST ; } 打破 ; //家长要求看,我们希望有多大是情况MeasureSpec。好评:if(childDimension > = 0){ //孩子想要一个特定的大小...让他拥有它resultSize = childDimension ; resultMode = MeasureSpec。确切地说 ; }
否则 ,如果(childDimension == 的LayoutParams。MATCH_PARENT){ //儿童希望成为我们的大小...找出它应该有多大//是resultSize = 0 ; resultMode = MeasureSpec。未知 ; } 否则如果(childDimension == 的LayoutParams。WRAP_CONTENT){ //孩子想确定自己的大小....找出如何应该//大是resultSize = 0 ; resultMode =
MeasureSpec。未知 ;
} 打破 ; } 返回MeasureSpec。makeMeasureSpec(resultSize,resultMode); }
针对上表,这里再做一下具体的说明
对于应用层View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定
对于不同的父容器和查看本身不同的LayoutParams,查看就可以有多种MeasureSpec.1。当前 视图采用固定宽高的时候,不管父容器的MeasureSpec是什么,查看的MeasureSpec都是精确模式并且其大小遵循Layoutparams中的大小; 2.当观的宽宽是match_parent时,这个时候如果父容器的模式是精准模式,那么观也是精准模式并且其大小是父容器的剩余空间,如果父容器是最大模式,那么查看也是最大模式并且其大小不会超过父容器的剩余空间; 3.当查的宽高是wrap_content时,不管父容器的模式是精准还是最大化,查看的模式总是最大化并且大小不能超过父容器的剩余空间.4。 未指明的模式,这个模式主要用于系统内部多次测量的情况下,一般来说,我们不需要关注此模式(这里注意自定义查看放到ScrollView的情况需要处理)。