定義:api
咱們能夠在Android的framework中的ViewGroup類裏找到定義的類:ide
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對象都可以使用如下參數:this
FILL_PARENT(api level 8及以上版本被改名爲MATCH_PARENT),它表示這個view想要和包含它的控件在此維度上採用一樣大小的尺寸spa
WRAP_CONTENT,它表示這個view在此維度上採用能夠包含它的內容的尺寸.net
精確尺寸code
有多個類繼承於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.getLayoutParams()的方式獲取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); }