RelativeLayout的onMeasure源码分析

来源:互联网 发布:淘宝赚钱 编辑:程序博客网 时间:2024/06/06 03:54

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

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

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

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











阅读全文
0 0
原创粉丝点击