Android View 原始碼解析(五) - RelativeLayout 原始碼分析
RelativeLayout 原始碼分析
繼承自ViewGroup 沒有過載onDraw方法 內部子View又是相對 只要計算出View的座標 layout過程同樣簡單
measure過程
主要過程
1.將內部View根據縱向關係和橫向關係排序
2.初始化相關變數
3.遍歷水平關係的View
4.遍歷豎直關係的View
5.baseline計算
6.寬度和高度修正
1 將內部View根據縱向關係和橫向關係排序
layout_toRightOf 為橫向關係 layout_below為縱向關係
//首先會根據mDirtyHierarchy的值判斷是否需要將子View重新排序 if (mDirtyHierarchy) { mDirtyHierarchy = false; sortChildren(); }
相關呼叫方法
//mDirtyHierarchy的值只有在requestLayout方法下被更新 public void requestLayout() { super.requestLayout(); mDirtyHierarchy = true; } //sortChildren()方法對橫向縱向關係的view的陣列進行非空判斷 用DependencyGraph進行判斷 private void sortChildren() { final int count = getChildCount(); 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.clear(); for (int i = 0; i < count; i++) { graph.add(getChildAt(i)); } graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL); graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL); }
DependencyGraph的相關方法
private static class DependencyGraph { ... /** * Adds a view to the graph. * * @param view The view to be added as a node to the graph. */ void add(View view) { //因為是圖 根據view生成一個節點 final int id = view.getId(); final Node node = Node.acquire(view); //如果是有效的id 則將該節點新增到List中 if (id != View.NO_ID) { mKeyNodes.put(id, node); } mNodes.add(node); } /** * 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. * * @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) { //當前View找不到其它的可依賴的View時作為root節點 final ArrayDeque<Node> roots = findRoots(rules); int index = 0; Node node; //讀取root的下一個node while ((node = roots.pollLast()) != null) { final View view = node.view; final int key = view.getId(); //將符合規則的View加到 sorted中 sorted[index++] = view; final ArrayMap<Node, DependencyGraph> dependents = node.dependents; //dependents 依賴該node的node(A C依賴B 則B的dependents中存A C) final int count = dependents.size(); //遍歷所有依賴自己的node for (int i = 0; i < count; i++) { final Node dependent = dependents.keyAt(i); //dependencies 是被依賴的的node的規則和node(A 依賴 B D 則dependencies存有B D ) final SparseArray<Node> dependencies = dependent.dependencies; //移除當前node和dependencies的依賴關係 dependencies.remove(key); //如果解除依賴後沒有其它依賴 則將該node也視為rootNode if (dependencies.size() == 0) { roots.add(dependent); } } } if (index < sorted.length) { throw new IllegalStateException("Circular dependencies cannot exist in RelativeLayout"); } } ... }
eg: A依賴B B依賴C 首先存入C 因為不依賴任何其它的
/** * Finds the roots of the graph. A root is a node with no dependency and * with [0..n] dependents. * * @param rulesFilter The list of rules to consider when building the *dependencies * * @return A list of node, each being a root of the graph */ private ArrayDeque<Node> findRoots(int[] rulesFilter) { //keyNodes為nodelist final SparseArray<Node> keyNodes = mKeyNodes; final ArrayList<Node> nodes = mNodes; final int count = nodes.size(); //初始化依賴該node的node和該node依賴的node相關引數 for (int i = 0; i < count; i++) { final Node node = nodes.get(i); node.dependents.clear(); node.dependencies.clear(); } //遍歷所有node存入當前view和他所依賴的關係 for (int i = 0; i < count; i++) { final Node node = nodes.get(i); final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); //取出當前View所有的依賴關係 final int[] rules = layoutParams.mRules; final int rulesCount = rulesFilter.length; //遍歷當前View所有的 for (int j = 0; j < rulesCount; j++) { //rule對應被依賴view的id final int rule = rules[rulesFilter[j]]; if (rule > 0) { //找到被依賴的node final Node dependency = keyNodes.get(rule); //跳過空view和本身 if (dependency == null || dependency == node) { continue; } //新增依賴被依賴的node dependency.dependents.put(node, this); node.dependencies.put(rule, dependency); } } } final ArrayDeque<Node> roots = mRoots; roots.clear(); // 再次遍歷如果該node的依賴關係為0 即該view不依賴任何view 則視為rootView for (int i = 0; i < count; i++) { final Node node = nodes.get(i); if (node.dependencies.size() == 0) roots.addLast(node); } return roots; }
2 初始化相關變數
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); // 如果不是UNSPECIFIED模式 則將widthSize賦值於myWidth if (widthMode != MeasureSpec.UNSPECIFIED) { myWidth = widthSize; } // 如果不是UNSPECIFIED模式 則將heightSize賦值於myHeight if (heightMode != MeasureSpec.UNSPECIFIED) { myHeight = heightSize; } //如果是EXACTLY模式 則將myWidth和myHeight記錄 if (widthMode == MeasureSpec.EXACTLY) { width = myWidth; } if (heightMode == MeasureSpec.EXACTLY) { height = myHeight; } View ignore = null; //判斷是否為Start 和top 確定左上角座標 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; // 記錄ignore的view if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } //寬度個高度是否為warp模式 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; //在計算和分配的子View的座標的時候 需要用到父VIew的尺寸 但是暫時無法拿到準確值(待完成下面操作) //先使用預設值代替 在計算後 用偏移量更新真是座標 final int layoutDirection = getLayoutDirection(); if (isLayoutRtl() && myWidth == -1) { myWidth = DEFAULT_WIDTH; }
3 遍歷水平關係的View
View[] views = mSortedHorizontalChildren; int count = views.length; for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); //根據方向獲得子View中設定的規則 int[] rules = params.getRules(layoutDirection); //將左右方向規則轉換為左右的座標 applyHorizontalSizeRules(params, myWidth, rules); //測算水平方向的子View的尺寸 measureChildHorizontal(child, params, myWidth, myHeight); //確定水平方向子View的位置 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { offsetHorizontalAxis = true; } } }
相關方法
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) { RelativeLayout.LayoutParams anchorParams; childParams.mLeft = VALUE_NOT_SET; childParams.mRight = VALUE_NOT_SET; //得到當前子View的layout_toLeftOf屬性對應的View anchorParams = getRelatedViewParams(rules, LEFT_OF); if (anchorParams != null) { //如果這個屬性存在 則當前子View的右座標是layout_toLeftOf對應的view的左座標減去對應view的marginLeft的值和自身marginRight的值 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin + childParams.rightMargin); //如果alignWithParent為true alignWithParent取alignWithParentIfMissing //如果layout_toLeftOf的view為空 或者gone 則將RelativeLayout當做被依賴的物件 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) { //如果父容器RelativeLayout的寬度大於0 //則子View的右座標為 父RelativeLayout的寬度減去 mPaddingRight 和自身的marginRight if (myWidth >= 0) { childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; } } //類似的方法 得到左座標(通過引數RIGHT_OF) 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; } //類似的方法 得到左座標 (通過引數ALIGN_LEFT) 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; } //類似的方法 得到右座標 (通過引數ALIGN_RIGHT) 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; } } //根據ALIGN_PARENT_LEFT 將自己放到父RelativeLayout的左邊 if (0 != rules[ALIGN_PARENT_LEFT]) { childParams.mLeft = mPaddingLeft + childParams.leftMargin; } //根據ALIGN_PARENT_RIGHT 將自己放到父RelativeLayout的右邊 if (0 != rules[ALIGN_PARENT_RIGHT]) { if (myWidth >= 0) { childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; } } }
private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) { //獲得child的寬度MeasureSpec final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight, myWidth); final int childHeightMeasureSpec; //在低於4.2的時候 mAllowBrokenMeasureSpecs為true //當myHeight < 0 時 則根據父RelativeLayout設定其MeasureSpec模式 if (myHeight < 0 && !mAllowBrokenMeasureSpecs) { //如果父RelativeLayout的height大於0則 設定子view的MeasureSpec模式為EXACTLY if (params.height >= 0) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( params.height, MeasureSpec.EXACTLY); } else { //反之 如果其小於0則設定子View的MeasureSpec為UNSPECIFIED childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } } else { //噹噹前myHeight >= 0 //判斷當前高度是否與父RelativeLayout高度相同 設定heightMode //根據maxHeight 和heightMode設定子View的MeasureSpec模式 final int maxHeight; if (mMeasureVerticalWithPaddingMargin) { maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom - params.topMargin - params.bottomMargin); } else { maxHeight = Math.max(0, myHeight); } final int heightMode; if (params.height == LayoutParams.MATCH_PARENT) { heightMode = MeasureSpec.EXACTLY; } else { heightMode = MeasureSpec.AT_MOST; } childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode); } //獲得了子View的WidthMeasureSpec和HeightMeasureSpec //子View可以通過measure方法獲取自身的size child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
/** * 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. * * @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; final boolean isUnspecified = mySize < 0; //如果父RelativeLayout寬度小於0 版本號不小於4.2 if (isUnspecified && !mAllowBrokenMeasureSpecs) { //如果子View的左邊距和右邊距都不為VALUE_NOT_SET //且右邊距座標大於左邊距座標 則將其差當做寬度賦予View 設定模式為EXACTLY //VALUE_NOT_SET = Integer.MIN_VALUE /** * Constant for the minimum {@code int} value, -2<sup>31</sup>. */ //public static final int MIN_VALUE = 0x80000000; if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) { childSpecSize = Math.max(0, childEnd - childStart); childSpecMode = MeasureSpec.EXACTLY; } else if (childSize >= 0) { // 如果childSpecSize >= 0 則賦值於childSpecSize //同樣設定模式為EXACTLY childSpecSize = childSize; childSpecMode = MeasureSpec.EXACTLY; } else { // 都不滿足則設定模式為UNSPECIFIED childSpecSize = 0; childSpecMode = MeasureSpec.UNSPECIFIED; } return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); } // 計算 開始和結束相關 int tempStart = childStart; int tempEnd = childEnd; //如果沒有指定start值 則預設賦予 padding和merage的值 if (tempStart == VALUE_NOT_SET) { tempStart = startPadding + startMargin; } //同上 if (tempEnd == VALUE_NOT_SET) { tempEnd = mySize - endPadding - endMargin; } //指定最大可提供的大小 final int maxAvailable = tempEnd - tempStart; if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) { //如果Start和End都是有效值 根據isUnspecified設定specMode為UNSPECIFIED或EXACTLY //並將設定對應的size childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY; childSpecSize = Math.max(0, maxAvailable); } else { //反之 判斷childSize的相關值 if (childSize >= 0) { //設定模式為EXACTLY //判斷maxAvailable和childSize情況 取較大值設定為childSpecSize childSpecMode = MeasureSpec.EXACTLY; if (maxAvailable >= 0) { // We have a maximum size in this dimension. childSpecSize = Math.min(maxAvailable, childSize); } else { // We can grow in this dimension. childSpecSize = childSize; } } else if (childSize == LayoutParams.MATCH_PARENT) { //如果子View是match模式 參照isUnspecified設定相關 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY; childSpecSize = Math.max(0, maxAvailable); } else if (childSize == LayoutParams.WRAP_CONTENT) { //在wrap進行設定 if (maxAvailable >= 0) { // We have a maximum size in this dimension. childSpecMode = MeasureSpec.AT_MOST; childSpecSize = maxAvailable; } else { // We can grow in this dimension. Child can be as big as it // wants. childSpecMode = MeasureSpec.UNSPECIFIED; childSpecSize = 0; } } } return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); }
以上 完成了View的第一次測量 確定了View的大小 然後根據大小覺得把子view放在父RelativeLayout中的位置
private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth, boolean wrapContent) { //獲取RelativeLayout的佈局方向 final int layoutDirection = getLayoutDirection(); int[] rules = params.getRules(layoutDirection); if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) { // 如果右邊界有效 左邊界無效 根據右邊界計算出左邊界 params.mLeft = params.mRight - child.getMeasuredWidth(); } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) { // 同上反之 params.mRight = params.mLeft + child.getMeasuredWidth(); } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) { //都無效的時候 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { //設定了CENTER_IN_PARENT或者 CENTER_HORIZONTAL的情況下 if (!wrapContent) { //非wrap情況下 //把子View水平中心固定在RelativeLayout的中心 centerHorizontal(child, params, myWidth); } else { //左邊距為padding+margin //右邊距為左邊距加上測量寬度 params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } return true; } else { //RTL右到左 佈局方向 //LTR�左到右 佈局方向 if (isLayoutRtl()) { params.mRight = myWidth - mPaddingRight- params.rightMargin; params.mLeft = params.mRight - child.getMeasuredWidth(); } else { params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } } } return rules[ALIGN_PARENT_END] != 0; //當為CENTER_IN_PARENTCENTER_HORIZONTAL ALIGN_PARENT_END三種情況之一時返回True }
4 遍歷豎直關係的View
... for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); //將豎直方向規則轉換為座標 applyVerticalSizeRules(params, myHeight, child.getBaseline()); //測量子View measureChild(child, params, myWidth, myHeight); //確定豎直方向子View的位置 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { offsetVerticalAxis = true; } //首先判斷是否為wrap模式 if (isWrapContentWidth) { //根據RTL或者LTR和版本進行區分 //Build.VERSION_CODES.KITKAT = 19 //主要對margin進行處理 if (isLayoutRtl()) { if (targetSdkVersion < Build.VERSION_CODES.KITKAT) { width = Math.max(width, myWidth - params.mLeft); } else { width = Math.max(width, myWidth - params.mLeft - params.leftMargin); } } else { if (targetSdkVersion < Build.VERSION_CODES.KITKAT) { width = Math.max(width, params.mRight); } else { width = Math.max(width, params.mRight + params.rightMargin); } } } if (isWrapContentHeight) { if (targetSdkVersion < Build.VERSION_CODES.KITKAT) { height = Math.max(height, params.mBottom); } else { height = Math.max(height, params.mBottom + params.bottomMargin); } } if (child != ignore || verticalGravity) { left = Math.min(left, params.mLeft - params.leftMargin); top = Math.min(top, params.mTop - params.topMargin); } if (child != ignore || horizontalGravity) { right = Math.max(right, params.mRight + params.rightMargin); bottom = Math.max(bottom, params.mBottom + params.bottomMargin); } } } ...
5 baseline計算
// Use the top-start-most laid out view as the baseline. RTL offsets are // applied later, so we can use the left-most edge as the starting edge. View baselineView = null; LayoutParams baselineParams = null; for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams childParams = (LayoutParams) child.getLayoutParams(); if (baselineView == null || baselineParams == null || compareLayoutPosition(childParams, baselineParams) < 0) { baselineView = child; baselineParams = childParams; } } } mBaselineView = baselineView;
6 寬度和高度修正
//如何是wrap模式 if (isWrapContentWidth) { width += mPaddingRight; if (mLayoutParams != null && mLayoutParams.width >= 0) { width = Math.max(width, mLayoutParams.width); } width = Math.max(width, getSuggestedMinimumWidth()); width = resolveSize(width, widthMeasureSpec); //在得到最後的width之後 對依賴RelativeLayout的子View添上偏移量 if (offsetHorizontalAxis) { for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); final int[] rules = params.getRules(layoutDirection); //對CENTER_IN_PARENT或者CENTER_HORIZONTAL的子View重測 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { centerHorizontal(child, params, width); //對ALIGN_PARENT_RIGHT重測 } else if (rules[ALIGN_PARENT_RIGHT] != 0) { final int childWidth = child.getMeasuredWidth(); params.mLeft = width - mPaddingRight - childWidth; params.mRight = params.mLeft + childWidth; } } } } } //同上 if (isWrapContentHeight) { height += mPaddingBottom; if (mLayoutParams != null && mLayoutParams.height >= 0) { height = Math.max(height, mLayoutParams.height); } height = Math.max(height, getSuggestedMinimumHeight()); height = resolveSize(height, heightMeasureSpec); if (offsetVerticalAxis) { for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); final int[] rules = params.getRules(layoutDirection); if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { centerVertical(child, params, height); } else if (rules[ALIGN_PARENT_BOTTOM] != 0) { final int childHeight = child.getMeasuredHeight(); params.mTop = height - mPaddingBottom - childHeight; params.mBottom = params.mTop + childHeight; } } } } } //根據gravity再次修正 if (horizontalGravity || verticalGravity) { final Rect selfBounds = mSelfBounds; selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight, height - mPaddingBottom); final Rect contentBounds = mContentBounds; Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds, layoutDirection); final int horizontalOffset = contentBounds.left - left; final int verticalOffset = contentBounds.top - top; if (horizontalOffset != 0 || verticalOffset != 0) { for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE && child != ignore) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); if (horizontalGravity) { params.mLeft += horizontalOffset; params.mRight += horizontalOffset; } if (verticalGravity) { params.mTop += verticalOffset; params.mBottom += verticalOffset; } } } } } //如果是RTL(右到左顯示)則再次修改 if (isLayoutRtl()) { final int offsetWidth = myWidth - width; for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() != GONE) { final LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mLeft -= offsetWidth; params.mRight -= offsetWidth; } } }
簡單總結
RelativeLayout更加關注子View的left right top bottom值 並且優先順序高於width和height
RelativeLayout的layout過程
對於RelativeLayout來的 layout過程更多的根據子View的left right top bottom值來設定位置
protected void onLayout(boolean changed, int l, int t, int r, int b) { //The layout has actually already been performed and the positions //cached.Apply the cached values to the children. final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { RelativeLayout.LayoutParams st = (RelativeLayout.LayoutParams) child.getLayoutParams(); child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom); } } }
RelativeLayout的draw過程
RelativeLayout作為ViewGroup的子類 因為其性質原因 沒有對draw過程進行修改
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。