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;
                }
            }
        }

第一次(水平方向)测量 到此为止 分析完毕

马上下面就来到了第二次(垂直方向)测量啦 非常非常类似

代码有点长 今天就分析到这里 未完待续











展开阅读全文

没有更多推荐了,返回首页