都知道RelativeLayout的一次測量調用兩次子視圖測量循環
橫向一次 縱向一次
帶着目的, 我們來分析源碼
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
sortChildren();
}
一上來就是重要代碼!
如果佈局層次是髒的(無效、或過時失效) 那麼sortChildren!!!! 這個方法是簡歷Relative佈局關係的核心
private void sortChildren() {
final int count = getChildCount();
//懶加載初始化兩個View數組 分別存放橫向 縱向
if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
mSortedVerticalChildren = new View[count];
}
if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
mSortedHorizontalChildren = new View[count];
}
final DependencyGraph graph = mGraph; //描述關係的graph
graph.clear();
//初始 清空graph
for (int i = 0; i < count; i++) {
graph.add(getChildAt(i)); //先都add收集
}
graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL); //再把兩個方向分別sort
graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
}
CoordinatorLayout裏的DAG這裏沒有使用, 而是用了一個內部類DependencyGraph描述graph private static class DependencyGraph {
mNodes存放所有一級子View
/**
* List of all views in the graph.
*/
private ArrayList<Node> mNodes = new ArrayList<Node>();
mKeyNodes存放有id的一級子View /**
* List of nodes in the graph. Each node is identified by its
* view id (see View#getId()).
*/
private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
mRoots臨時數據結構,用於有序的放置目標的View數組,注意:這裏的數據結構是ArrayDeque 雙端隊列 爲什麼要用這個數據結構 在getSortedViews裏會講到 /**
* Temporary data structure used to build the list of roots
* for this graph.
*/
private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
循環收集“”一級”子View的方法 add(注意是一級,二級里加dependency xml也會給你報錯!)
/**
* Adds a view to the graph. 把view都收集在graph裏
*
* @param view The view to be added as a node to the graph.
*/
void add(View view) {
final int id = view.getId();
final Node node = Node.acquire(view); //內部類Node描述節點
if (id != View.NO_ID) {
mKeyNodes.put(id, node); //所有有id的View都會被存放在mKeyNodes裏,因爲有可能被依賴
}
mNodes.add(node); //所有的子View都收集在mNodes裏
}
核心方法getSortedViews排序後放入View數組裏 /**
* Builds a sorted list of views. The sorting order depends on the dependencies
* between the view. For instance, if view C needs view A to be processed first
* and view A needs view B to be processed first, the dependency graph
* is: B -> A -> C. The sorted array will contain views B, A and C in this order.
* 創建有序的view數組。“序”取決於View之間的dependencies關係。
* 例如,如果viewC的佈局需要先viewA先處理,而viewA的佈局又需要先viewB先處理
* 那麼依賴圖就是:B -> A -> C 有序數組將這麼排序:{viewB,viewA, viewC}。
*
* @param sorted The sorted list of views. The length of this array must
* be equal to getChildCount().
* @param rules The list of rules to take into account.
*/
void getSortedViews(View[] sorted, int... rules) {
final ArrayDeque<Node> roots = findRoots(rules); //找出所有的沒有依賴的node 就是root
int index = 0;
Node node;
// 啥是pollLast: 獲取並移除此列表尾部的最後一個元素
while ((node = roots.pollLast()) != null) { //把roots根節點依次pollLast出來
final View view = node.view;
final int key = view.getId();
sorted[index++] = view; //然後設置到數組的對應位置
//是不是搞錯了 爲什麼只加了根節點?別急
// 在這個方法內部 所有的根節點rootA被設置完之後,
//實際上會把(被rootA直接依賴的)rootB加在roots尾部,進行下一次while循環...
//直到這個rootN沒有被依賴的root了,纔會while到下一個findRoots方法出來的無依賴root
final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
final int count = dependents.size(); //找到這個node的被依賴樹graph
for (int i = 0; i < count; i++) {
final Node dependent = dependents.keyAt(i);
final SparseArray<Node> dependencies = dependent.dependencies;
dependencies.remove(key); //優化,移除掉處理過的root
if (dependencies.size() == 0) { //把被依賴樹的頂層(也就是當前root的直接被依賴)
//作爲下一次while循環的root對象 add到roots的尾部
roots.add(dependent);
}
}
}
if (index < sorted.length) { //如果數組沒設置滿就跳出了while循環,
//說明有循環依賴! 源碼是會拋出異常的!!!
throw new IllegalStateException("Circular dependencies cannot exist"
+ " in RelativeLayout");
}
}
findRoots方法private的,暫放置,以後再分析。
此時,所有的橫向上的依賴“序”都被排序進了mSortedHorizontalChildren;所有的縱向上的依賴“序”都被排序進了mSortedVerticalChildren
回來看onMeasure 下面是一段局部變量的初始化把orientation specSize specMode等信息都初始化出來
int myWidth = -1;
int myHeight = -1;
int width = 0;
int height = 0;
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Record our dimensions if they are known;
if (widthMode != MeasureSpec.UNSPECIFIED) {
myWidth = widthSize;
}
if (heightMode != MeasureSpec.UNSPECIFIED) {
myHeight = heightSize;
}
if (widthMode == MeasureSpec.EXACTLY) {
width = myWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = myHeight;
}
View ignore = null;
int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
int left = Integer.MAX_VALUE;
int top = Integer.MAX_VALUE;
int right = Integer.MIN_VALUE;
int bottom = Integer.MIN_VALUE;
boolean offsetHorizontalAxis = false;
boolean offsetVerticalAxis = false;
if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
ignore = findViewById(mIgnoreGravity);
}
final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
以上沒有什麼重點代碼
下面處理RTL模式
// We need to know our size for doing the correct computation of children positioning in RTL
// mode but there is no practical way to get it instead of running the code below.
// So, instead of running the code twice, we just set the width to a "default display width"
// before the computation and then, as a last pass, we will update their real position with
// an offset equals to "DEFAULT_WIDTH - width".
// 我們需要知道在正確的計算RTL模式下子View定位時我們自己的大小,
// 但實際上沒有捷徑來獲得,只能運行下面的代碼。而不是運行代碼的兩倍!
// 我們只是在計算前設置寬度爲“默認顯示寬度”,之後作爲最後一關,
// 我們將更新他們的真實位置的偏移量等於“DEFAULT_WIDTH - width”
final int layoutDirection = getLayoutDirection();
if (isLayoutRtl() && myWidth == -1) {
myWidth = DEFAULT_WIDTH;
}
進入正題! 先循環測量水平方向的
//先循環測量水平方向的
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) { //GONE的child是不會被measure的
LayoutParams params = (LayoutParams) child.getLayoutParams();
int[] rules = params.getRules(layoutDirection); //取出子view的lp水平方向的所有rule
// 這個getRules裏的mRules看起來是個臨時輸出變量
//把水平方向上的align_left left_of right_of等等 全部替換成
//padding margin mLeft mRight之類的像素屬性
applyHorizontalSizeRules(params, myWidth, rules);
//這裏調用子view的measure方法
measureChildHorizontal(child, params, myWidth, myHeight);
//最終全部替換成mLeft mRight屬性
if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
}
}
依次分析三個主要方法applyHorizontalSizeRules、 measureChildHorizontal、 positionChildHorizontal
嗯 先是applyHorizontalSizeRules
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
RelativeLayout.LayoutParams anchorParams;
// VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
// left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
// wants to the right
// left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
// wants to the left
// left=10, right=20 means the left and right ends are both fixed
// VALUE_NOT_SET表示“軟要求”在那個方向。例如:
// 左爲10,右爲VALUE_NOT_SET指view必須從10開始,往右邊想走多遠走多遠
// 左= VALUE_NOT_SET,右= 10 意味着view必須結束在10,但左邊想走多遠走多遠
// 左= 10,右= 20 意味着左右兩端都是固定的。
childParams.mLeft = VALUE_NOT_SET;
childParams.mRight = VALUE_NOT_SET;
anchorParams = getRelatedViewParams(rules, LEFT_OF); //下面我們就拿這個屬性來先做分析
if (anchorParams != null) { //
childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
childParams.rightMargin);
} else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
//以下有類同
anchorParams = getRelatedViewParams(rules, RIGHT_OF);
if (anchorParams != null) {
childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
childParams.leftMargin);
} else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
if (anchorParams != null) {
childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
} else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
if (anchorParams != null) {
childParams.mRight = anchorParams.mRight - childParams.rightMargin;
} else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
if (0 != rules[ALIGN_PARENT_LEFT]) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
if (0 != rules[ALIGN_PARENT_RIGHT]) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
}
這裏我們就拿第一個rule LEFT_OF屬性的apply來分析舉例, 其他幾個屬性RIGHT_OF ALIGN_LEFT ALIGN_RIGHT ALIGN_PARENT_LEFT ALIGN_PARENT_RIGHT 舉一反三即可
anchorParams = getRelatedViewParams(rules, LEFT_OF); //我們就拿LEFT_OF來分析舉例
if (anchorParams != null) { //
//child的lp的右邊屬性將被賦值爲 = 錨點(rule所依賴)View的左邊屬性
childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
childParams.rightMargin);
} else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
//注意 特例child有LEFT_OF屬性但getRelatedViewParams又沒有找到可見的(不爲GONE)依賴錨點(下面分析有說明原因)
//這時候 如果設置了alignWithParent 那麼child會視爲對齊parentLeft
//那麼問題來了 這個alignWithParent是什麼鬼? 從哪裏冒出來的?
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
alignWithParent尋蹤?
老套路 如果是lp屬性
generatorLayoutParams會調用RelativeLayout.LayoutParams的構造方法 ,從attr裏取出
我們來看
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.RelativeLayout_Layout);
final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
!c.getApplicationInfo().hasRtlSupport());
final int[] rules = mRules;
//noinspection MismatchedReadAndWriteOfArray
final int[] initialRules = mInitialRules;
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
alignWithParent = a.getBoolean(attr, false);//原來有這麼個屬性 是不是以前沒用過?
break;
case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
rules[LEFT_OF] = a.getResourceId(attr, 0);
果然有哎~~~ 打開layout文件的XML編輯器 嘿~ 還真有這屬性 漲姿勢
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="demo2.fd.com.demo2application.MainActivity">
<TextView
android:layout_alignWithParentIfMissing="true"
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
上面提到了apply操作主要來自使用getRelatedViewParams方法尋找是否有可見的依賴View的lp,追進這個方法
private LayoutParams getRelatedViewParams(int[] rules, int relation) {
View v = getRelatedView(rules, relation); //尋找可用的rule依賴對象View
if (v != null) {
ViewGroup.LayoutParams params = v.getLayoutParams();
if (params instanceof LayoutParams) {
return (LayoutParams) v.getLayoutParams();
}
}
return null;
}
找lp先找view 找到了view 那麼lp手到擒來 追進getRelatedView方法
private View getRelatedView(int[] rules, int relation) {
int id = rules[relation];
if (id != 0) { //尋找目標rules依賴的view的id
//之前提到過(見DependencyGraph分析) 有id的View 應該從mKeyNodes這個集合裏找
DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
if (node == null) return null;
View v = node.view; //找到node>>取出rule對應的依賴view>>返回
// Find the first non-GONE view up the chain
//如果這個依賴view是GONE的話! rule依賴是不會生效的
//注意! 但是以下while代碼會繼續尋找依賴鏈!
// 比如有一個childView尋找自己的LEFT_OF屬性找到了R.id.title,是GONE的;
// 但這個R.id.title也有LEFT_OF屬性, 依賴於R.id.sub_title
// 是可見的~ 那麼childView會根據LEFT_OF依賴於R.id.sub_title進行measure
while (v.getVisibility() == View.GONE) {
rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
node = mGraph.mKeyNodes.get((rules[relation]));
if (node == null) return null;
v = node.view;
}
return v;
}
return null;
}
到此爲止 applyHorizontalSizeRules方法的操作全揭祕 拖走 下一個主要方法 measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight)
measureChildHorizontal方法主要分爲三部分
第一部分 計算寬度MeasureSpec 通過getChildMeasureSpec方法
第二部分 計算高度MeasureSpec 注意:這裏只是臨時measure,意思一下。 後面還會專門再算一遍高度
第三部分 當然就是調用他啦child.measure
private void measureChildHorizontal(
View child, LayoutParams params, int myWidth, int myHeight) {
//1.先計算寬度MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
myWidth);
//2. 再計算高度MeasureSpec
final int childHeightMeasureSpec;
//mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
//也就是說sdk 4.2及以前 mAllowBrokenMeasureSpecs這個值是true 之後是false
// myHeight判斷負值 下面有說明
if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
if (params.height >= 0) { //RelativeLayout沒有具體高度但子view的lp有具體高度就按exactly來
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
params.height, MeasureSpec.EXACTLY);
} else {
// 在RelativeLayout的測量過程中,mySize/myWidth/myWidth中的負值意味着:
// 我們從spec中獲取了一個UNSPECIFIED模式
// Negative values in a mySize/myWidth/myWidth value in
// RelativeLayout measurement is code for, "we got an
// unspecified mode in the RelativeLayout's measure spec."
// Carry it forward.
RelativeLayout
//子view的lp和RelativeLayout都沒有具體高度用UNSPECIFIED模式去測量
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
} else { //4.3以後新SDK下 或RelativeLayout給了具體高
final int maxHeight;
if (mMeasureVerticalWithPaddingMargin) {
maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
- params.topMargin - params.bottomMargin);
} else {
maxHeight = Math.max(0, myHeight);
} //高度取0和myHeight的最大值
final int heightMode;
if (params.height == LayoutParams.MATCH_PARENT) {
heightMode = MeasureSpec.EXACTLY; //充滿時exactly模式
} else {
heightMode = MeasureSpec.AT_MOST; //其他at_molst
}
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
}
//調用子view的measure
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
第二部分不是我們現在分析的重點 我們現在正處於社會主義初級階段 第一遍(水平方向)測量
所以我們重點來看第一部分getChildMeasureSpec方法的調用
這個方法是水平/垂直兩用的!因爲現在是水平測量,所以我們看到這裏的調用:
getChildMeasureSpec(params.mLeft, params.mRight,
params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
myWidth);
清一色的全部是lp.mLeft lp.mRight lp.leftMargin lp.rightMargin, mPaddingLeft, mPaddingRight, 以及水平方向上的父佈局size: myWidth /**
* Get a measure spec that accounts for all of the constraints on this view.
* This includes size constraints imposed by the RelativeLayout as well as
* the View's desired dimension.
獲取一個度量規範spec來描述此view的所有約束。
這包括RelativeLayout施加的尺寸約束、以及view自己的尺寸訴求。
*
* @param childStart The left or top field of the child's layout params
* @param childEnd The right or bottom field of the child's layout params
* @param childSize The child's desired size (the width or height field of
* the child's layout params)
* @param startMargin The left or top margin
* @param endMargin The right or bottom margin
* @param startPadding mPaddingLeft or mPaddingTop
* @param endPadding mPaddingRight or mPaddingBottom
* @param mySize The width or height of this view (the RelativeLayout)
* @return MeasureSpec for the child
*/
private int getChildMeasureSpec(int childStart, int childEnd,
int childSize, int startMargin, int endMargin, int startPadding,
int endPadding, int mySize) {
int childSpecMode = 0;
int childSpecSize = 0;
// Negative values in a mySize value in RelativeLayout
// measurement is code for, "we got an unspecified mode in the
// RelativeLayout's measure spec."
// 跟之前解釋的一樣 負的mySize代表UNSPECIFIED模式
final boolean isUnspecified = mySize < 0;
//isUnspecified代表 RelativeLayout的寬度有沒有被指定
if (isUnspecified && !mAllowBrokenMeasureSpecs) {
if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
//childStart是左上 childEnd是右下
//如果之前applyHorizontalSizeRules處理過的左右都有依賴
//也就是lp.mLeft和lp.mRight都有值,那麼被夾緊的寬度 就是exactly的
//注意! 在RelativeLayout裏這是優先於指定寬度的
// Constraints fixed both edges, so child has an exact size.
childSpecSize = Math.max(0, childEnd - childStart);
childSpecMode = MeasureSpec.EXACTLY;
} else if (childSize >= 0) {
//指定的寬度 當然也是exactly的
// The child specified an exact size.
childSpecSize = childSize;
childSpecMode = MeasureSpec.EXACTLY;
} else {
//RelativeLayout的寬度有沒有被指定 child的lp又沒法知道確定高度
//那隻好UNSPECIFIED了
// Allow the child to be whatever size it wants.
childSpecSize = 0;
childSpecMode = MeasureSpec.UNSPECIFIED;
}
return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
}
//sdk4.3及以上 或者指定了elativeLayout的寬度 才能進入以下代碼
// Figure out start and end bounds.
int tempStart = childStart;
int tempEnd = childEnd;
//因爲這時候mySize有值(sdk4.3及以上 不是有可能沒值嘛? 這裏存疑)
//如果左右沒有依賴view,也就是說apply之前沒有給lp賦值mLeft或mRight的話
//把左右先賦值(RelativeLayout的mySize去掉padding和margin)
// If the view did not express a layout constraint for an edge, use
// view's margins and our padding
if (tempStart == VALUE_NOT_SET) {
tempStart = startPadding + startMargin;
}
if (tempEnd == VALUE_NOT_SET) {
tempEnd = mySize - endPadding - endMargin;
}
//子view寬度的極限 右-左咯
// Figure out maximum size available to this view
final int maxAvailable = tempEnd - tempStart;
if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
// Constraints fixed both edges, so child must be an exact size.
childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
//mode取決於RelativeLayout的size
//也就是說 4.3以上 未指定寬度的RelativeLayout這裏用UNSPECIFIED 否則用EXACTLY
childSpecSize = Math.max(0, maxAvailable); //跟之前邏輯一樣 有明確的左右夾緊依賴
//這裏也是優先於子View自己訴求寬度的
} else {
if (childSize >= 0) {
//lp裏有明確的子View訴求寬度 EXACTLY
// Child wanted an exact size. Give as much as possible.
childSpecMode = MeasureSpec.EXACTLY;
if (maxAvailable >= 0) {
// We have a maximum size in this dimension.
//如果maxAvailable比子View訴求寬度還小 用maxAvailable
//比如左右都沒有依賴view的時候,maxAvailable就應該等於
//RelativeLayout的寬度-padding-margin
//結果RelativeLayout父佈局滿足不了那麼子view要的那麼寬 就能給多少給多少
childSpecSize = Math.min(maxAvailable, childSize);
} else {
// We can grow in this dimension.
childSpecSize = childSize;
}
} else if (childSize == LayoutParams.MATCH_PARENT) {
//子View沒有明確訴求下分兩種情況 要麼MATCH_PARENT 要麼WRAP_CONTENT
// MATCH_PARENT的情況下 父佈局RelativeLayout能給多少給多少
// Child wanted to be as big as possible. Give all available
// space.
childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
childSpecSize = Math.max(0, maxAvailable);
} else if (childSize == LayoutParams.WRAP_CONTENT) {
//子View沒有明確訴求下 子view是WRAP_CONTENT的情況
// Child wants to wrap content. Use AT_MOST to communicate
// available space if we know our max size.
if (maxAvailable >= 0) {
// We have a maximum size in this dimension.
childSpecMode = MeasureSpec.AT_MOST; //就只能給到AT_MOST咯
childSpecSize = maxAvailable;
} else {
// We can grow in this dimension. Child can be as big as it
// wants.
childSpecMode = MeasureSpec.UNSPECIFIED;
childSpecSize = 0;
}
}
}
第一次(水平方向)測量 到此爲止 分析完畢
馬上下面就來到了第二次(垂直方向)測量啦 非常非常類似
代碼有點長 今天就分析到這裏 未完待續