RelativeLayout的onMeasure源碼分析

都知道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;
                }
            }
        }

第一次(水平方向)測量 到此爲止 分析完畢

馬上下面就來到了第二次(垂直方向)測量啦 非常非常類似

代碼有點長 今天就分析到這裏 未完待續











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章