定义:
我们可以在
Android
的
framework
中的
ViewGroup
类里找到定义的类:
1 public static class LayoutParams{...}
此类有如下注释:
LayoutParams are used by views to tell their parents how they want to be laid out.
View
对象使用
LayoutParams
对象来告知其上层控件自己需要多少空间。
The base LayoutParams class just describes how big the view wants to be for both width and height.
基础LayoutParams类只是定义了这个view
需要占用的宽度和高度。
For each dimension, it can specify one of: FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which means that the view wants to be as big as its parent (minus padding) WRAP_CONTENT, which means that the view wants to be just big enough to enclose its content (plus padding) an exact number There are subclasses of LayoutParams for different subclasses of ViewGroup. For example, AbsoluteLayout has its own subclass of LayoutParams which adds an X and Y value.
对于每一个维度,也就是对于宽度和高度,LayoutParams对象均可以使用以下参数:
FILL_PARENT(api level 8
及以上版本被更名为
MATCH_PARENT),它表示这个view
想要和包含它的控件在此维度上采用同样大小的尺寸
WRAP_CONTENT,它表示这个view
在此维度上采用可以包含它的内容的尺寸
精确尺寸
有多个类继承于
ViewGroup.
LayoutParams类,比如,AbsoluteLayout类中定义了LayoutParams,它继承于ViewGroup.LayoutParams类
Android
中,直接继承于
ViewGroup.
LayoutParams类的有:ViewGroup.MarginLayoutParams,间接继承的类有:Linearlayout.LayoutParams,RelativeLayout.LayoutParams,FrameLayout.LayoutParams等等。
使用
1
:
在代码中动态添加
view
可以采用如下方式:
1 TextView tv = new TextView(this); 2 tv.setText("hello world"); 3 linearlayout.addView(tv);
此时就将tv
按照默认的布局方式添加进
viewgroup
中了,这里的
viewgroup
具体就是
linearlayout
了。
那么这里只是采用了一句
addview(view)
,没有传入任何的布局参数,那么默认的布局参数是什么呢?
代码跟踪:
在
ViewGroup
类中函数的定义以及注释:
/*Adds a child view. If no layout parameters are already set on the child, the default parameters for this ViewGroup are set on the child. 添加一个view。如果这个view没有layout parameters参数定义,那么就采取默认参数。 */
public void addView(View child) {
addView(child, -1);
}
解读:
addView(View child)
函数直接调用了
addView(View child, int index)。
public void addView(View child, int index) { LayoutParams params = child.getLayoutParams(); if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params); }
解读:使用
view.get
LayoutParams()的方式获取set
在此
view
对象中的参数,如果此参数是空值,就通过
generateDefaultLayoutParams()的方式产生一个LayoutParams。查看generateDefaultLayoutParams()
函数的定义:
ViewGroup
中定义如下:
protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); }
仅仅返回一个固定形式的LayoutParam
,
文章开始时说了,LayoutParams有多个间接子类,那么进入LinearLayout
中,看看是否
override
了此
generateDefaultLayoutParams()
函数。
LinearLayout中定义如下:
@Override protected LayoutParams generateDefaultLayoutParams() { if (mOrientation == HORIZONTAL) { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } else if (mOrientation == VERTICAL) { return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } return null; }
解读:根据LinearLayout的不同方向返回不同的LayoutParams对象,注意这里的LayoutParams对象的类型为LinearLayout.LayoutParams。
我们返回addView(View child, int index)函数继续跟踪:
public void addView(View child, int index, LayoutParams params) { if (DBG) { System.out.println(this + " addView"); } // addViewInner() will call child.requestLayout() when setting the new LayoutParams // therefore, we call requestLayout() on ourselves before, so that the child's request // will be blocked at our level requestLayout(); invalidate(true); addViewInner(child, index, params, false); }
//
解释说明见:
http://stackoverflow.com/questions/21863631/calling-viewgroupaddview-or-viewgroupremoveview-from-viewdraw
requestLayout()函数的作用是,强制此view
向上一直
requestLayout
,使得
view
调用
measure
和
layout
(参考:
http://blog.csdn.net/djun100/article/details/11917777
)
invalidate(true)函数的作用是,重新draw
此
view
。
再看addViewInner()
函数:
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { //当ViewGroup中的object改变时,mTransition用以处理动画效果,此对象的类是LayoutTransition if (mTransition != null) { // Don't prevent other add transitions from completing, but cancel remove // transitions to let them complete the process before we add to the container mTransition.cancel(LayoutTransition.DISAPPEARING); } //addView(view)时会检查view是不是具有ViewParent,如果有,就会抛出下面的异常,一个view只能有一个ViewParent。注:ViewParent是一个接口,ViewGroup实现了此接口。 if (child.getParent() != null) { throw new IllegalStateException("The specified child already has a parent. " + "You must call removeView() on the child's parent first."); } //这里的addChild并非是将view添加至ViewGroup中,这里是在处理动画效果 if (mTransition != null) { mTransition.addChild(this, child); } //检查params是否为空 if (!checkLayoutParams(params)) { params = generateLayoutParams(params); } //preventRequestLayout参数的含义是:是否禁止这个child去requestLayout(),原因是当使用直接赋值的时候,不会触发任何方法,但是当使用setLayoutParams()方法时,此方法中会去调用requestLayout()。不过View中的mLayoutParams参数被注解成了hide,无法在我们自定义的类中直接赋值。 if (preventRequestLayout) { child.mLayoutParams = params; } else { child.setLayoutParams(params); } if (index < 0) { index = mChildrenCount; } //addInArray()方法是将child添加到ViewGroup的mChildren对象中,mChildren是一个View[]类对象。 addInArray(child, index); //assignParent()函数给child分配指定parent,并进行requestLayout(),采用preventRequestLayout进行判断,与上边的setLayoutParams()结合,可以保证child只调用一个requestLayout() // tell our children if (preventRequestLayout) { child.assignParent(this); } else { child.mParent = this; } //焦点 if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } //和view所处环境相关的参数的设置 AttachInfo ai = mAttachInfo; if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { needGlobalAttributesUpdate(true); } ai.mKeepScreenOn = lastKeepOn; } if (child.isLayoutDirectionInherited()) { child.resetRtlProperties(); } //回调 onViewAdded(child); if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; } if (child.hasTransientState()) { childHasTransientStateChanged(child, true); } if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } } //addViewInner()函数中涉及到的setLayoutParams()的具体实现,可以看到view调用了requestLayout()。在我们分析的这个addView()方法中,由于view的parent是null,因此mParent instanceof ViewGroup 返回的是false。 public void setLayoutParams(ViewGroup.LayoutParams params) { if (params == null) { throw new NullPointerException("Layout parameters cannot be null"); } mLayoutParams = params; resolveLayoutParams(); if (mParent instanceof ViewGroup) { ((ViewGroup) mParent).onSetLayoutParams(this, params); } requestLayout(); }
回过头来再看一下addView()
的实现:
public void addView(View child, int index, LayoutParams params) { // addViewInner() will call child.requestLayout() when setting the new LayoutParams // therefore, we call requestLayout() on ourselves before, so that the child's request // will be blocked at our level requestLayout(); invalidate(true); addViewInner(child, index, params, false); }