ViewGroup的官方解析是:
A <code>ViewGroup</code> is a special view that can contain other views (called children.) The view group is the base class for layouts and views containers.
其类定义如下:
public abstract class ViewGroup extends View implements ViewParent, ViewManager
首先是View的子类,并且实现两个接口,这两个接口中分别包含着极为重要的方法,可以说代表着ViewGroup的核心特性。
ViewManager接口定义:
1 package android.view; 2 3 /** Interface to let you add and remove child views to an Activity. To get an instance 4 * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. 5 */ 6 public interface ViewManager 7 { 8 public void addView(View view, ViewGroup.LayoutParams params); 9 public void updateViewLayout(View view, ViewGroup.LayoutParams params); 10 public void removeView(View view); 11 }
ViewParent接口定义:
1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import android.graphics.Rect; 20 21 /** 22 * Defines the responsibilities for a class that will be a parent of a View. 23 * This is the API that a view sees when it wants to interact with its parent. 24 * 25 */ 26 public interface ViewParent { 27 /** 28 * Called when something has changed which has invalidated the layout of a 29 * child of this view parent. This will schedule a layout pass of the view 30 * tree. 31 */ 32 public void requestLayout(); 33 34 /** 35 * Indicates whether layout was requested on this view parent. 36 * 37 * @return true if layout was requested, false otherwise 38 */ 39 public boolean isLayoutRequested(); 40 41 /** 42 * Called when a child wants the view hierarchy to gather and report 43 * transparent regions to the window compositor. Views that "punch" holes in 44 * the view hierarchy, such as SurfaceView can use this API to improve 45 * performance of the system. When no such a view is present in the 46 * hierarchy, this optimization in unnecessary and might slightly reduce the 47 * view hierarchy performance. 48 * 49 * @param child the view requesting the transparent region computation 50 * 51 */ 52 public void requestTransparentRegion(View child); 53 54 /** 55 * All or part of a child is dirty and needs to be redrawn. 56 * 57 * @param child The child which is dirty 58 * @param r The area within the child that is invalid 59 */ 60 public void invalidateChild(View child, Rect r); 61 62 /** 63 * All or part of a child is dirty and needs to be redrawn. 64 * 65 * The location array is an array of two int values which respectively 66 * define the left and the top position of the dirty child. 67 * 68 * This method must return the parent of this ViewParent if the specified 69 * rectangle must be invalidated in the parent. If the specified rectangle 70 * does not require invalidation in the parent or if the parent does not 71 * exist, this method must return null. 72 * 73 * When this method returns a non-null value, the location array must 74 * have been updated with the left and top coordinates of this ViewParent. 75 * 76 * @param location An array of 2 ints containing the left and top 77 * coordinates of the child to invalidate 78 * @param r The area within the child that is invalid 79 * 80 * @return the parent of this ViewParent or null 81 */ 82 public ViewParent invalidateChildInParent(int[] location, Rect r); 83 84 /** 85 * Returns the parent if it exists, or null. 86 * 87 * @return a ViewParent or null if this ViewParent does not have a parent 88 */ 89 public ViewParent getParent(); 90 91 /** 92 * Called when a child of this parent wants focus 93 * 94 * @param child The child of this ViewParent that wants focus. This view 95 * will contain the focused view. It is not necessarily the view that 96 * actually has focus. 97 * @param focused The view that is a descendant of child that actually has 98 * focus 99 */ 100 public void requestChildFocus(View child, View focused); 101 102 /** 103 * Tell view hierarchy that the global view attributes need to be 104 * re-evaluated. 105 * 106 * @param child View whose attributes have changed. 107 */ 108 public void recomputeViewAttributes(View child); 109 110 /** 111 * Called when a child of this parent is giving up focus 112 * 113 * @param child The view that is giving up focus 114 */ 115 public void clearChildFocus(View child); 116 117 public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset); 118 119 /** 120 * Find the nearest view in the specified direction that wants to take focus 121 * 122 * @param v The view that currently has focus 123 * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT 124 */ 125 public View focusSearch(View v, int direction); 126 127 /** 128 * Change the z order of the child so it's on top of all other children 129 * 130 * @param child 131 */ 132 public void bringChildToFront(View child); 133 134 /** 135 * Tells the parent that a new focusable view has become available. This is 136 * to handle transitions from the case where there are no focusable views to 137 * the case where the first focusable view appears. 138 * 139 * @param v The view that has become newly focusable 140 */ 141 public void focusableViewAvailable(View v); 142 143 /** 144 * Bring up a context menu for the specified view or its ancestors. 145 * <p> 146 * In most cases, a subclass does not need to override this. However, if 147 * the subclass is added directly to the window manager (for example, 148 * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)}) 149 * then it should override this and show the context menu. 150 * 151 * @param originalView The source view where the context menu was first invoked 152 * @return true if a context menu was displayed 153 */ 154 public boolean showContextMenuForChild(View originalView); 155 156 /** 157 * Have the parent populate the specified context menu if it has anything to 158 * add (and then recurse on its parent). 159 * 160 * @param menu The menu to populate 161 */ 162 public void createContextMenu(ContextMenu menu); 163 164 /** 165 * This method is called on the parent when a child's drawable state 166 * has changed. 167 * 168 * @param child The child whose drawable state has changed. 169 */ 170 public void childDrawableStateChanged(View child); 171 172 /** 173 * Called when a child does not want this parent and its ancestors to 174 * intercept touch events with 175 * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}. 176 * <p> 177 * This parent should pass this call onto its parents. This parent must obey 178 * this request for the duration of the touch (that is, only clear the flag 179 * after this parent has received an up or a cancel. 180 * 181 * @param disallowIntercept True if the child does not want the parent to 182 * intercept touch events. 183 */ 184 public void requestDisallowInterceptTouchEvent(boolean disallowIntercept); 185 186 /** 187 * Called when a child of this group wants a particular rectangle to be 188 * positioned onto the screen. {@link ViewGroup}s overriding this can trust 189 * that: 190 * <ul> 191 * <li>child will be a direct child of this group</li> 192 * <li>rectangle will be in the child's coordinates</li> 193 * </ul> 194 * 195 * <p>{@link ViewGroup}s overriding this should uphold the contract:</p> 196 * <ul> 197 * <li>nothing will change if the rectangle is already visible</li> 198 * <li>the view port will be scrolled only just enough to make the 199 * rectangle visible</li> 200 * <ul> 201 * 202 * @param child The direct child making the request. 203 * @param rectangle The rectangle in the child's coordinates the child 204 * wishes to be on the screen. 205 * @param immediate True to forbid animated or delayed scrolling, 206 * false otherwise 207 * @return Whether the group scrolled to handle the operation 208 */ 209 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, 210 boolean immediate); 211 }
可以看到addView, removeView, requestLayout, bringChildToFront等等重要的方法都在这两个借口里面得到定义
一、addView方法
所有的addView方法最后都集中到一个方法:
1 /** 2 * Adds a child view with the specified layout parameters. 3 * 4 * @param child the child view to add 5 * @param index the position at which to add the child 6 * @param params the layout parameters to set on the child 7 */ 8 public void addView(View child, int index, LayoutParams params) { 9 if (DBG) { 10 System.out.println(this + " addView"); 11 } 12 13 // addViewInner() will call child.requestLayout() when setting the new LayoutParams 14 // therefore, we call requestLayout() on ourselves before, so that the child's request 15 // will be blocked at our level 16 requestLayout(); 17 invalidate(); 18 addViewInner(child, index, params, false); 19 }
requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure onLayout来对重新设置自己位置。这里首先安调用自己的requestLayout告诉自己的父View要求重新布局,然后调用invalidate(调用onDraw())要求重绘,然后调用addViewInner去调用child的requestLayout,这个方法的源码如下:
1 private void addViewInner(View child, int index, LayoutParams params, 2 boolean preventRequestLayout) { 3 4 if (child.getParent() != null) { 5 throw new IllegalStateException("The specified child already has a parent. " + 6 "You must call removeView() on the child's parent first."); 7 } 8 9 if (!checkLayoutParams(params)) { 10 params = generateLayoutParams(params); 11 } 12 13 if (preventRequestLayout) { 14 child.mLayoutParams = params; 15 } else { 16 child.setLayoutParams(params); 17 } 18 19 if (index < 0) { 20 index = mChildrenCount; 21 } 22 23 addInArray(child, index); 24 25 // tell our children 26 if (preventRequestLayout) { 27 child.assignParent(this); 28 } else { 29 child.mParent = this; 30 } 31 32 if (child.hasFocus()) { 33 requestChildFocus(child, child.findFocus()); 34 } 35 36 AttachInfo ai = mAttachInfo; 37 if (ai != null) { 38 boolean lastKeepOn = ai.mKeepScreenOn; 39 ai.mKeepScreenOn = false; 40 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); 41 if (ai.mKeepScreenOn) { 42 needGlobalAttributesUpdate(true); 43 } 44 ai.mKeepScreenOn = lastKeepOn; 45 } 46 47 if (mOnHierarchyChangeListener != null) { 48 mOnHierarchyChangeListener.onChildViewAdded(this, child); 49 } 50 51 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { 52 mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; 53 } 54 }
这里可以看到我们写代码的时候经常看到的一个异常,也解释了index的具体含义。我们也可以看到:每一个View都保存了一个指向父View的指针。倒数第二段代码,也用到一个接口,可以监听子View的添加和删除:
1 /** 2 * Interface definition for a callback to be invoked when the hierarchy 3 * within this view changed. The hierarchy changes whenever a child is added 4 * to or removed from this view. 5 */ 6 public interface OnHierarchyChangeListener { 7 /** 8 * Called when a new child is added to a parent view. 9 * 10 * @param parent the view in which a child was added 11 * @param child the new child view added in the hierarchy 12 */ 13 void onChildViewAdded(View parent, View child); 14 15 /** 16 * Called when a child is removed from a parent view. 17 * 18 * @param parent the view from which the child was removed 19 * @param child the child removed from the hierarchy 20 */ 21 void onChildViewRemoved(View parent, View child); 22 }
二、removeView
removeView方法除了正常调用requestLayout和invalidate方法以外:
1 /** 2 * Removes the specified range of views from the group. 3 * 4 * @param start the first position in the group of the range of views to remove 5 * @param count the number of views to remove 6 */ 7 public void removeViews(int start, int count) { 8 removeViewsInternal(start, count); 9 requestLayout(); 10 invalidate(); 11 }
但是看下面两个方法:
1 /** 2 * {@inheritDoc} 3 */ 4 public void removeView(View view) { 5 removeViewInternal(view); 6 requestLayout(); 7 invalidate(); 8 } 9 10 /** 11 * Removes a view during layout. This is useful if in your onLayout() method, 12 * you need to remove more views. 13 * 14 * @param view the view to remove from the group 15 */ 16 public void removeViewInLayout(View view) { 17 removeViewInternal(view); 18 }
后者没有调用requestLayout和invalidate,但是后者的说明里面提到是在onLayout()方法中调用这个方法的。所以如果没有猜错的话,在布局完成后调用后面一个方法对界面是没有影响的,好的转入正题:
1 private void removeViewInternal(int index, View view) { 2 boolean clearChildFocus = false; 3 if (view == mFocused) { 4 view.clearFocusForRemoval(); 5 clearChildFocus = true; 6 } 7 8 if (view.getAnimation() != null) { 9 addDisappearingView(view); 10 } else if (view.mAttachInfo != null) { 11 view.dispatchDetachedFromWindow(); 12 } 13 14 if (mOnHierarchyChangeListener != null) { 15 mOnHierarchyChangeListener.onChildViewRemoved(this, view); 16 } 17 18 needGlobalAttributesUpdate(false); 19 20 removeFromArray(index); 21 22 if (clearChildFocus) { 23 clearChildFocus(view); 24 } 25 }
这里的删除貌似只是从Array中删除了一个元素(更加印证了前面提到的说法)。只有调用onLayout和onMeasure方法之后才会诱发界面重新布局。
三、测量函数
1 /** 2 * Ask all of the children of this view to measure themselves, taking into 3 * account both the MeasureSpec requirements for this view and its padding. 4 * We skip children that are in the GONE state The heavy lifting is done in 5 * getChildMeasureSpec. 6 * 7 * @param widthMeasureSpec The width requirements for this view 8 * @param heightMeasureSpec The height requirements for this view 9 */ 10 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 11 final int size = mChildrenCount; 12 final View[] children = mChildren; 13 for (int i = 0; i < size; ++i) { 14 final View child = children[i]; 15 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { 16 measureChild(child, widthMeasureSpec, heightMeasureSpec); 17 } 18 } 19 } 20 21 /** 22 * Ask one of the children of this view to measure itself, taking into 23 * account both the MeasureSpec requirements for this view and its padding. 24 * The heavy lifting is done in getChildMeasureSpec. 25 * 26 * @param child The child to measure 27 * @param parentWidthMeasureSpec The width requirements for this view 28 * @param parentHeightMeasureSpec The height requirements for this view 29 */ 30 protected void measureChild(View child, int parentWidthMeasureSpec, 31 int parentHeightMeasureSpec) { 32 final LayoutParams lp = child.getLayoutParams(); 33 34 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 35 mPaddingLeft + mPaddingRight, lp.width); 36 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 37 mPaddingTop + mPaddingBottom, lp.height); 38 39 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 40 }
前者只不过是告诉这个Group把自己所有的View全部测量一下,后者则是测量单个子View的大小。这边无疑涉及到一个方法:
1 /** 2 * Does the hard part of measureChildren: figuring out the MeasureSpec to 3 * pass to a particular child. This method figures out the right MeasureSpec 4 * for one dimension (height or width) of one child view. 5 * 6 * The goal is to combine information from our MeasureSpec with the 7 * LayoutParams of the child to get the best possible results. For example, 8 * if the this view knows its size (because its MeasureSpec has a mode of 9 * EXACTLY), and the child has indicated in its LayoutParams that it wants 10 * to be the same size as the parent, the parent should ask the child to 11 * layout given an exact size. 12 * 13 * @param spec The requirements for this view 14 * @param padding The padding of this view for the current dimension and 15 * margins, if applicable 16 * @param childDimension How big the child wants to be in the current 17 * dimension 18 * @return a MeasureSpec integer for the child 19 */ 20 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 21 int specMode = MeasureSpec.getMode(spec); 22 int specSize = MeasureSpec.getSize(spec); 23 24 int size = Math.max(0, specSize - padding); 25 26 int resultSize = 0; 27 int resultMode = 0; 28 29 switch (specMode) { 30 // Parent has imposed an exact size on us 31 case MeasureSpec.EXACTLY: 32 if (childDimension >= 0) { 33 resultSize = childDimension; 34 resultMode = MeasureSpec.EXACTLY; 35 } else if (childDimension == LayoutParams.MATCH_PARENT) { 36 // Child wants to be our size. So be it. 37 resultSize = size; 38 resultMode = MeasureSpec.EXACTLY; 39 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 40 // Child wants to determine its own size. It can't be 41 // bigger than us. 42 resultSize = size; 43 resultMode = MeasureSpec.AT_MOST; 44 } 45 break; 46 47 // Parent has imposed a maximum size on us 48 case MeasureSpec.AT_MOST: 49 if (childDimension >= 0) { 50 // Child wants a specific size... so be it 51 resultSize = childDimension; 52 resultMode = MeasureSpec.EXACTLY; 53 } else if (childDimension == LayoutParams.MATCH_PARENT) { 54 // Child wants to be our size, but our size is not fixed. 55 // Constrain child to not be bigger than us. 56 resultSize = size; 57 resultMode = MeasureSpec.AT_MOST; 58 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 59 // Child wants to determine its own size. It can't be 60 // bigger than us. 61 resultSize = size; 62 resultMode = MeasureSpec.AT_MOST; 63 } 64 break; 65 66 // Parent asked to see how big we want to be 67 case MeasureSpec.UNSPECIFIED: 68 if (childDimension >= 0) { 69 // Child wants a specific size... let him have it 70 resultSize = childDimension; 71 resultMode = MeasureSpec.EXACTLY; 72 } else if (childDimension == LayoutParams.MATCH_PARENT) { 73 // Child wants to be our size... find out how big it should 74 // be 75 resultSize = 0; 76 resultMode = MeasureSpec.UNSPECIFIED; 77 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 78 // Child wants to determine its own size.... find out how 79 // big it should be 80 resultSize = 0; 81 resultMode = MeasureSpec.UNSPECIFIED; 82 } 83 break; 84 } 85 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 86 }
三种模式的判断,进行不同的赋值!这些值会传递给子View,告诉它们父View的大小。
四、布局参数LayoutParams
1 /** 2 * LayoutParams are used by views to tell their parents how they want to be 3 * laid out. See 4 * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes} 5 * for a list of all child view attributes that this class supports. 6 * 7 * <p> 8 * The base LayoutParams class just describes how big the view wants to be 9 * for both width and height. For each dimension, it can specify one of: 10 * <ul> 11 * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which 12 * means that the view wants to be as big as its parent (minus padding) 13 * <li> WRAP_CONTENT, which means that the view wants to be just big enough 14 * to enclose its content (plus padding) 15 * <li> an exact number 16 * </ul> 17 * There are subclasses of LayoutParams for different subclasses of 18 * ViewGroup. For example, AbsoluteLayout has its own subclass of 19 * LayoutParams which adds an X and Y value. 20 * 21 * @attr ref android.R.styleable#ViewGroup_Layout_layout_height 22 * @attr ref android.R.styleable#ViewGroup_Layout_layout_width 23 */ 24 public static class LayoutParams { 25 /** 26 * Special value for the height or width requested by a View. 27 * FILL_PARENT means that the view wants to be as big as its parent, 28 * minus the parent's padding, if any. This value is deprecated 29 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 30 */ 31 @SuppressWarnings({"UnusedDeclaration"}) 32 @Deprecated 33 public static final int FILL_PARENT = -1; 34 35 /** 36 * Special value for the height or width requested by a View. 37 * MATCH_PARENT means that the view wants to be as big as its parent, 38 * minus the parent's padding, if any. Introduced in API Level 8. 39 */ 40 public static final int MATCH_PARENT = -1; 41 42 /** 43 * Special value for the height or width requested by a View. 44 * WRAP_CONTENT means that the view wants to be just large enough to fit 45 * its own internal content, taking its own padding into account. 46 */ 47 public static final int WRAP_CONTENT = -2; 48 49 /** 50 * Information about how wide the view wants to be. Can be one of the 51 * constants FILL_PARENT (replaced by MATCH_PARENT , 52 * in API Level 8) or WRAP_CONTENT. or an exact size. 53 */ 54 @ViewDebug.ExportedProperty(mapping = { 55 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 56 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 57 }) 58 public int width; 59 60 /** 61 * Information about how tall the view wants to be. Can be one of the 62 * constants FILL_PARENT (replaced by MATCH_PARENT , 63 * in API Level 8) or WRAP_CONTENT. or an exact size. 64 */ 65 @ViewDebug.ExportedProperty(mapping = { 66 @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), 67 @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") 68 }) 69 public int height; 70 71 /** 72 * Used to animate layouts. 73 */ 74 public LayoutAnimationController.AnimationParameters layoutAnimationParameters; 75 76 /** 77 * Creates a new set of layout parameters. The values are extracted from 78 * the supplied attributes set and context. The XML attributes mapped 79 * to this set of layout parameters are: 80 * 81 * <ul> 82 * <li><code>layout_width</code>: the width, either an exact value, 83 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 84 * {@link #MATCH_PARENT} in API Level 8)</li> 85 * <li><code>layout_height</code>: the height, either an exact value, 86 * {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by 87 * {@link #MATCH_PARENT} in API Level 8)</li> 88 * </ul> 89 * 90 * @param c the application environment 91 * @param attrs the set of attributes from which to extract the layout 92 * parameters' values 93 */ 94 public LayoutParams(Context c, AttributeSet attrs) { 95 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout); 96 setBaseAttributes(a, 97 R.styleable.ViewGroup_Layout_layout_width, 98 R.styleable.ViewGroup_Layout_layout_height); 99 a.recycle(); 100 } 101 102 /** 103 * Creates a new set of layout parameters with the specified width 104 * and height. 105 * 106 * @param width the width, either {@link #WRAP_CONTENT}, 107 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 108 * API Level 8), or a fixed size in pixels 109 * @param height the height, either {@link #WRAP_CONTENT}, 110 * {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in 111 * API Level 8), or a fixed size in pixels 112 */ 113 public LayoutParams(int width, int height) { 114 this.width = width; 115 this.height = height; 116 } 117 118 /** 119 * Copy constructor. Clones the width and height values of the source. 120 * 121 * @param source The layout params to copy from. 122 */ 123 public LayoutParams(LayoutParams source) { 124 this.width = source.width; 125 this.height = source.height; 126 } 127 128 /** 129 * Used internally by MarginLayoutParams. 130 * @hide 131 */ 132 LayoutParams() { 133 } 134 135 /** 136 * Extracts the layout parameters from the supplied attributes. 137 * 138 * @param a the style attributes to extract the parameters from 139 * @param widthAttr the identifier of the width attribute 140 * @param heightAttr the identifier of the height attribute 141 */ 142 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 143 width = a.getLayoutDimension(widthAttr, "layout_width"); 144 height = a.getLayoutDimension(heightAttr, "layout_height"); 145 } 146 147 /** 148 * Returns a String representation of this set of layout parameters. 149 * 150 * @param output the String to prepend to the internal representation 151 * @return a String with the following format: output + 152 * "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }" 153 * 154 * @hide 155 */ 156 public String debug(String output) { 157 return output + "ViewGroup.LayoutParams={ width=" 158 + sizeToString(width) + ", height=" + sizeToString(height) + " }"; 159 } 160 161 /** 162 * Converts the specified size to a readable String. 163 * 164 * @param size the size to convert 165 * @return a String instance representing the supplied size 166 * 167 * @hide 168 */ 169 protected static String sizeToString(int size) { 170 if (size == WRAP_CONTENT) { 171 return "wrap-content"; 172 } 173 if (size == MATCH_PARENT) { 174 return "match-parent"; 175 } 176 return String.valueOf(size); 177 } 178 }
注解:
1)从API-LEVEL8开始,FILL_PARENT被MATCH_PARENT代替;
2)留下一个疑问:这些布局参数到底是如何变成布局尺寸的?