Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析

今天,我着重讲解下如下三个内容:

  1. measure过程
  2. WRAP_CONTENT、MATCH_PARENT/FILL_PARENT属性的原理说明
  3. xml布局文件解析成View树的流程分析。

希望对大家能有帮助。- - 分析版本基于Android 2.3 。

1、WRAP_CONTENT、MATCH_PARENT/FILL_PARENT

初入Android殿堂的同学们,对这三个属性一定又爱又恨。爱的是使用起来挺爽地---照葫芦画瓢即可,恨的

却是时常混淆这几个属性地意义,需要三思而后行。在带着大家重温下这几个属性的用法吧(希望我没有啰嗦)。

这三个属性都用来适应视图的水平或垂直大小,一个以视图的内容或尺寸为基础的布局比精确地指定视图范围

更加方便。

① fill_parent

设置一个视图的布局为fill_parent将强制性地使视图扩展至父元素大小。

② match_parent

Android 中match_parent和fill_parent意思一样,但match_parent更贴切,于是从2.2开始两个词都可以

用,但2.3版本后建议使用match_parent。

③ wrap_content

自适应大小,强制性地使视图扩展以便显示其全部内容。以TextView和ImageView控件为例,设置为

wrap_content将完整显示其内部的文本和图像。布局元素将根据内容更改大小。

可不要重复造轮子,以上摘自<<Android fill_parent、wrap_content和match_parent的区别>>。

当然,我们可以设置View的确切宽高,而不是由以上属性指定。

01.android:layout_weight="wrap_content"   //自适应大小  
02.android:layout_weight="match_parent"   //与父视图等高  
03.android:layout_weight="fill_parent"    //与父视图等高  
04.android:layout_weight="100dip"         //精确设置高度值为 100dip  

接下来,我们需要转换下视角,看看ViewGroup.LayoutParams类及其派生类。

2、ViewGroup.LayoutParams类及其派生类

2.1、 ViewGroup.LayoutParams类说明

Android API中如下介绍:

LayoutParams are used by views to tell their parents how they want to be laid out.

意思大概是说: View通过LayoutParams类告诉其父视图它想要地大小(即,长度和宽度)。

因此,每个View都包含一个ViewGroup.LayoutParams类或者其派生类,View类依赖于ViewGroup.LayoutParams。

路径:frameworks\base\core\java\android\view\View.java

01.public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {  
02.  ...  
03.  /** 
04.   * The layout parameters associated with this view and used by the parent 
05.   * {@link android.view.ViewGroup} to determine how this view should be 
06.   * laid out. 
07.   * {@hide} 
08.   */  
09.  //该View拥有的 LayoutParams属性,父试图添加该View时,会为其赋值,特别注意,其类型为ViewGroup.LayoutParams。  
10.  protected ViewGroup.LayoutParams mLayoutParams;    
11.  ...  
12.}  

2.2、 ViewGroup.LayoutParams源码分析

路径位于:frameworks\base\core\java\android\view\ViewGroup.java

01.public abstract class ViewGroup extends View implements ViewParent, ViewManager {  
02.    ...  
03.     public static class LayoutParams {  
04.        /** 
05.         * Special value for the height or width requested by a View. 
06.         * FILL_PARENT means that the view wants to be as big as its parent, 
07.         * minus the parent's padding, if any. This value is deprecated 
08.         * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 
09.         */  
10.        @Deprecated  
11.        public static final int FILL_PARENT = -1;  // 注意值为-1,Android2.2版本不建议使用  
12.        /** 
13.         * Special value for the height or width requested by a View. 
14.         * MATCH_PARENT means that the view wants to be as big as its parent, 
15.         * minus the parent's padding, if any. Introduced in API Level 8. 
16.         */  
17.        public static final int MATCH_PARENT = -1; // 注意值为-1  
18.        /** 
19.         * Special value for the height or width requested by a View. 
20.         * WRAP_CONTENT means that the view wants to be just large enough to fit 
21.         * its own internal content, taking its own padding into account. 
22.         */  
23.        public static final int WRAP_CONTENT = -2; // 注意值为-2  
24.        /** 
25.         * Information about how wide the view wants to be. Can be one of the 
26.         * constants FILL_PARENT (replaced by MATCH_PARENT , 
27.         * in API Level 8) or WRAP_CONTENT. or an exact size. 
28.         */  
29.        public int width;  //该View的宽度,可以为WRAP_CONTENT/MATCH_PARENT 或者一个具体值  
30.        /** 
31.         * Information about how tall the view wants to be. Can be one of the 
32.         * constants FILL_PARENT (replaced by MATCH_PARENT , 
33.         * in API Level 8) or WRAP_CONTENT. or an exact size. 
34.         */  
35.        public int height; //该View的高度,可以为WRAP_CONTENT/MATCH_PARENT 或者一个具体值  
36.        /** 
37.         * Used to animate layouts. 
38.         */  
39.        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;  
40.        /** 
41.         * Creates a new set of layout parameters. The values are extracted from 
42.         * the supplied attributes set and context. The XML attributes mapped 
43.         * to this set of layout parameters are:、 
44.         */  
45.        public LayoutParams(Context c, AttributeSet attrs) {  
46.            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);  
47.            setBaseAttributes(a,  
48.                    R.styleable.ViewGroup_Layout_layout_width,  
49.                    R.styleable.ViewGroup_Layout_layout_height);  
50.            a.recycle();  
51.        }  
52.  
53.        /** 
54.         * Creates a new set of layout parameters with the specified width 
55.         * and height. 
56.         */  
57.        public LayoutParams(int width, int height) {  
58.            this.width = width;  
59.            this.height = height;  
60.        }  
61.        /** 
62.         * Copy constructor. Clones the width and height values of the source. 
63.         * 
64.         * @param source The layout params to copy from. 
65.         */  
66.        public LayoutParams(LayoutParams source) {  
67.            this.width = source.width;  
68.            this.height = source.height;  
69.        }  
70.        /** 
71.         * Used internally by MarginLayoutParams. 
72.         * @hide 
73.         */  
74.        LayoutParams() {  
75.        }  
76.        /** 
77.         * Extracts the layout parameters from the supplied attributes. 
78.         * 
79.         * @param a the style attributes to extract the parameters from 
80.         * @param widthAttr the identifier of the width attribute 
81.         * @param heightAttr the identifier of the height attribute 
82.         */  
83.        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {  
84.            width = a.getLayoutDimension(widthAttr, "layout_width");  
85.            height = a.getLayoutDimension(heightAttr, "layout_height");  
86.        }  
87.}  

我们发现FILL_PARENT/MATCH_PARENT值为 -1 ,WRAP_CONETENT值为-2,是不是有点诧异? 将值

设置为负值的目的是为了区别View的具体值(an exact size) 总是大于0的。

ViewGroup子类可以实现自定义LayoutParams,自定义LayoutParams提供了更好地扩展性,例如LinearLayout

就有LinearLayout. LayoutParams自定义类(见下文)。整个LayoutParams类家族还是挺复杂的。

ViewGroup.LayoutParams及其常用派生类的类图(部分类图)如下:

该类图是在太庞大了,大家有兴趣的去看看Android API吧。

前面我们说过,每个View都包含一个ViewGroup.LayoutParams类或者其派生类,下面我们的疑问是Android框架

中时如何为View设置其LayoutParams属性的。

有两种方法会设置View的LayoutParams属性:

1、 直接添加子View时,常见于如下几种方法:ViewGroup.java

01.//Adds a child view.      
02.void addView(View child, int index)  
03.//Adds a child view with this ViewGroup's default layout parameters   
04.//and the specified width and height.  
05.void addView(View child, int width, int height)  
06.//Adds a child view with the specified layout parameters.         
07.void addView(View child, ViewGroup.LayoutParams params)  

三个重载方法的区别只是添加View时构造LayoutParams对象的方式不同而已,稍后我们探寻一下它们的源码。

2、 通过xml布局文件指定某个View的属性为:android:layout_heigth=””以及android:layout_weight=”” 时。

总的来说,这两种方式都会设定View的LayoutParams属性值----指定的或者Default值。

方式1流程分析:

直接添加子View时,比较容易理解,我们先来看看这种方式设置LayoutParams的过程:

路径:\frameworks\base\core\java\android\view\ViewGroup.java

01.public abstract class ViewGroup extends View implements ViewParent, ViewManager {  
02.    ...  
03.    /** 
04.     * Adds a child view. If no layout parameters are already set on the child, the 
05.     * default parameters for this ViewGroup are set on the child. 
06.     * 
07.     * @param child the child view to add 
08.     * 
09.     * @see #generateDefaultLayoutParams() 
10.     */  
11.    public void addView(View child) {  
12.        addView(child, -1);  
13.    }  
14.    /** 
15.     * Adds a child view. If no layout parameters are already set on the child, the 
16.     * default parameters for this ViewGroup are set on the child. 
17.     * 
18.     * @param child the child view to add 
19.     * @param index the position at which to add the child 
20.     * 
21.     * @see #generateDefaultLayoutParams() 
22.     */  
23.    public void addView(View child, int index) {  
24.        LayoutParams params = child.getLayoutParams();  
25.        if (params == null) {  
26.            params = generateDefaultLayoutParams(); //返回默认地LayoutParams类,作为该View的属性值  
27.            if (params == null) {//如果不能获取到LayoutParams对象,则抛出异常。  
28.                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");  
29.            }  
30.        }  
31.        addView(child, index, params);  
32.    }  
33.    /** 
34.     * Adds a child view with this ViewGroup's default layout parameters and the 
35.     * specified width and height. 
36.     * 
37.     * @param child the child view to add 
38.     */  
39.    public void addView(View child, int width, int height) {  
40.        //返回默认地LayoutParams类,作为该View的属性值  
41.        final LayoutParams params = generateDefaultLayoutParams();   
42.        params.width = width;   //重新设置width值  
43.        params.height = height; //重新设置height值  
44.        addView(child, -1, params); //这儿,我们有指定width、height的大小了。  
45.    }  
46.    /** 
47.     * Adds a child view with the specified layout parameters. 
48.     * 
49.     * @param child the child view to add 
50.     * @param params the layout parameters to set on the child 
51.     */  
52.    public void addView(View child, LayoutParams params) {  
53.        addView(child, -1, params);  
54.    }  
55.    /** 
56.     * Adds a child view with the specified layout parameters. 
57.     * 
58.     * @param child the child view to add 
59.     * @param index the position at which to add the child 
60.     * @param params the layout parameters to set on the child 
61.     */  
62.    public void addView(View child, int index, LayoutParams params) {  
63.        ...  
64.        // addViewInner() will call child.requestLayout() when setting the new LayoutParams  
65.        // therefore, we call requestLayout() on ourselves before, so that the child's request  
66.        // will be blocked at our level  
67.        requestLayout();  
68.        invalidate();  
69.        addViewInner(child, index, params, false);  
70.    }  
71.    /** 
72.     * Returns a set of default layout parameters. These parameters are requested 
73.     * when the View passed to {@link #addView(View)} has no layout parameters 
74.     * already set. If null is returned, an exception is thrown from addView. 
75.     * 
76.     * @return a set of default layout parameters or null 
77.     */  
78.    protected LayoutParams generateDefaultLayoutParams() {  
79.        //width 为 WRAP_CONTENT大小 , height 为WRAP_CONTENT   
80.        //ViewGroup的子类可以重写该方法,达到其特定要求。稍后会以LinearLayout类为例说明。  
81.        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
82.    }  
83.    private void addViewInner(View child, int index, LayoutParams params,  
84.            boolean preventRequestLayout) {  
85.  
86.        if (!checkLayoutParams(params)) { //params对象是否为null  
87.            params = generateLayoutParams(params); //如果params对象是为null,重新构造个LayoutParams对象  
88.        }  
89.        //preventRequestLayout值为false  
90.        if (preventRequestLayout) {    
91.            child.mLayoutParams = params; //为View的mLayoutParams属性赋值  
92.        } else {  
93.            child.setLayoutParams(params);//为View的mLayoutParams属性赋值,但会调用requestLayout()请求重新布局  
94.        }  
95.        //if else 语句会设置View为mLayoutParams属性赋值  
96.        ...  
97.    }  
98.    ...  
99.}  

主要功能就是在添加子View时为其构建了一个LayoutParams对象。但更重要的是,ViewGroup的子类可以重载上面的几个方法,返回特定的LayoutParams对象,例如:对于LinearLayout而言,则是LinearLayout.LayoutParams对象。这么做地目的是,能在其他需要它的地方,可以将其强制转换成LinearLayout.LayoutParams对象。

LinearLayout重写函数地实现为:

01.public class LinearLayout extends ViewGroup {  
02.    ...  
03.    @Override  
04.    public LayoutParams generateLayoutParams(AttributeSet attrs) {  
05.        return new LinearLayout.LayoutParams(getContext(), attrs);  
06.    }  
07.    @Override  
08.    protected LayoutParams generateDefaultLayoutParams() {  
09.        //该LinearLayout是水平方向还是垂直方向  
10.        if (mOrientation == HORIZONTAL) {   
11.            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
12.        } else if (mOrientation == VERTICAL) {  
13.            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);  
14.        }  
15.        return null;  
16.    }  
17.    @Override  
18.    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {  
19.        return new LayoutParams(p);  
20.    }  
21.    /** 
22.     * Per-child layout information associated with ViewLinearLayout. 
23.     *  
24.     * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight 
25.     * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity 
26.     */ //自定义的LayoutParams类  
27.    public static class LayoutParams extends ViewGroup.MarginLayoutParams {  
28.        /** 
29.         * Indicates how much of the extra space in the LinearLayout will be 
30.         * allocated to the view associated with these LayoutParams. Specify 
31.         * 0 if the view should not be stretched. Otherwise the extra pixels 
32.         * will be pro-rated among all views whose weight is greater than 0. 
33.         */  
34.        @ViewDebug.ExportedProperty(category = "layout")  
35.        public float weight;      //  见于属性,android:layout_weight=""  ;  
36.        /** 
37.         * Gravity for the view associated with these LayoutParams. 
38.         * 
39.         * @see android.view.Gravity 
40.         */  
41.        public int gravity = -1;  // 见于属性, android:layout_gravity=""  ;   
42.        /** 
43.         * {@inheritDoc} 
44.         */  
45.        public LayoutParams(Context c, AttributeSet attrs) {  
46.            super(c, attrs);  
47.            TypedArray a =c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);  
48.            weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);  
49.            gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);  
50.  
51.            a.recycle();  
52.        }  
53.        /** 
54.         * {@inheritDoc} 
55.         */  
56.        public LayoutParams(int width, int height) {  
57.            super(width, height);  
58.            weight = 0;  
59.        }  
60.        /** 
61.         * Creates a new set of layout parameters with the specified width, height 
62.         * and weight. 
63.         * 
64.         * @param width the width, either {@link #MATCH_PARENT}, 
65.         *        {@link #WRAP_CONTENT} or a fixed size in pixels 
66.         * @param height the height, either {@link #MATCH_PARENT}, 
67.         *        {@link #WRAP_CONTENT} or a fixed size in pixels 
68.         * @param weight the weight 
69.         */  
70.        public LayoutParams(int width, int height, float weight) {  
71.            super(width, height);  
72.            this.weight = weight;  
73.        }  
74.        public LayoutParams(ViewGroup.LayoutParams p) {  
75.            super(p);  
76.        }  
77.        public LayoutParams(MarginLayoutParams source) {  
78.            super(source);  
79.        }  
80.    }  
81.    ...  
82.} 

LinearLayout.LayoutParams类继承至ViewGroup.MarginLayoutParams类,添加了对android:layout_weight以及 android:layout_gravity这两个属性的获取和保存。而且它的重写函数返回的都是LinearLayout.LayoutParams类型。这样,我们可以再对子View进行其他操作时,可以将将其强制转换成LinearLayout.LayoutParams对象进行使用。

例如,LinearLayout进行measure过程,使用了LinearLayout.LayoutParam对象,有如下代码:

01.public class LinearLayout extends ViewGroup {  
02.    ...  
03.    @Override  //onMeasure方法。  
04.    protected void onMea
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值