Weex Android 源码分析之 CSS LayoutEngine

1.背景

    之前分析了下Weex Android渲染的逻辑,最后在DOM完成之后,会调用DomActionContext的batch方法,里面会调用LayoutEngine.layoutNode方法进行布局,细看了下,这个方法的实现虽然很长,但理清了过程还是很有意思的。

     LayoutEngine里面主要有2个方法:layoutNode\layoutNodeImpl, 主要的实现是在layoutNodeImpl里面,下面我们也主要看这个方法。

    在分析之前需要知道CSS Flex的布局方式与语法,因为本来LayoutEngine就是处理CSS相关属性的节点的布局的。

推荐阅读阮老师的教程

2.基础流程

    layoutNodeImpl方法内部主要由7个循环(A-G)实现,下面简单说下7个循环 (A-G) 的作用,便于理解代码:

  1. Loop A : 确定非flexible Children的布局,同时统计各个类型的Children的个数。(三种类型:flexible、non flexible、absolutely)
  2. Loop B : 确定flexible Children的布局,同时分配剩余的空间(根据Flex或者justifyContent)  。 
  3. Loop C : 确定主轴方向上Children的位置以及计算容器的尺寸。
  4. Loop D : 确定侧轴方向上Children的位置。
  5. Loop E : 多行情况下,确定侧轴方向Children的位置(事实上可以替代Loop D)。
  6. Loop F : 需要的话,完善Children位置的信息。
  7. Loop G :计算绝对布局元素的尺寸。

实际上还有个大循环,包括了A-D,用于多行处理的,不属于单独功能性的,所以不计入上述。循环的结构如下:

while(){
   A
   B
   C
   D
}
  E
  F
  G

OK,上述流程大概了解之后,我们可以去分析源码了,Yeah,Show me the Code ! 

3.源码分析

    3.1 坐标引用

    不过别着急看源码,看源码之前先了解点知识,有助于理解代码。首先Weex是支持LTR / RTL两种布局方式的,所以LayoutEngine里对这方面也进行了处理,那么怎么处理呢?;其次Flex 布局有2个方向(横向、纵向),因此主轴可能是横向的、也可能是纵向的,代码也要考虑这种情况,这是个问题。

那么这两个问题怎么方便的解决呢?

    首先说问题一、为了支持LTR \ RTL,所以代码中设置了4个布局方向:COLUMN (0)/ COLUMN_REVERSE (1)/ ROW(2) / ROW_REVERSE (3),用来表示这几种情况,虽然COLUMN_REVERSE可能用不着。

    其次看问题二、实际上代码中不用考虑是横向纵向,主要分清楚是主轴和侧轴就行了,所以代码里获取元素都是根据主轴和侧轴。

    值得一提的是:代码里面对坐标做了个映射。举个例子:

要获取主轴方向元素的dimension信息的代码是这样的:

node.cssstyle.dimensions[dim[mainAxis]]

其中mainAxis是上述4个布局方向之一(0-3),dim数组是预先定义好的,值是:

 private static final int[] dim = {

            DIMENSION_HEIGHT,
            DIMENSION_HEIGHT,
            DIMENSION_WIDTH,
            DIMENSION_WIDTH,

    };

  public static final int DIMENSION_WIDTH = 0;

  public static final int DIMENSION_HEIGHT = 1;

可以看到,当主轴是column方向的时候,主轴的dimension是高度。


3.2 源码

OK,下面我们就开始看源码,先从layoutNode方法开始:

    /*package*/
    static void layoutNode(
            CSSLayoutContext layoutContext, 
            CSSNode node,  //WXDomObject
            float parentMaxWidth,  //UNDEFINED
            CSSDirection parentDirection) { // NULL
        if (needsRelayout(node, parentMaxWidth)) { //需要重新布局
            node.lastLayout.requestedWidth = node.csslayout.dimensions[DIMENSION_WIDTH];
            node.lastLayout.requestedHeight = node.csslayout.dimensions[DIMENSION_HEIGHT];
            node.lastLayout.parentMaxWidth = parentMaxWidth;

            layoutNodeImpl(layoutContext, node, parentMaxWidth, parentDirection); 
            node.updateLastLayout(node.csslayout);
        } else {
            node.csslayout.copy(node.lastLayout); //复制上一个布局结果
            node.updateLastLayout(node.lastLayout);//nothing changed
        }

        node.markHasNewLayout();
    }

可以看到如果需要重新计算的话,那么会调用layoutNodeImpl重新计算,否则啥也不干。简单看一下needsRelayout的实现:

    static boolean needsRelayout(CSSNode node, float parentMaxWidth) {
        return node.isDirty() ||
                !FloatUtil.floatsEqual(  //上个布局结果的要求高度/宽度/parent最大宽度  不等于  当前layout结果的高度/宽度/parent最大宽度
                        node.lastLayout.requestedHeight,
                        node.csslayout.dimensions[DIMENSION_HEIGHT]) ||
                !FloatUtil.floatsEqual(
                        node.lastLayout.requestedWidth,
                        node.csslayout.dimensions[DIMENSION_WIDTH]) ||
                !FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth);
    }

当node 是dirty状态  或者上次布局需要的宽高发生变化、parent最大width发生改变的时候会触发重新布局的逻辑。

接下来,让我们看下layoutNodeImpl方法,一个700行左右的方法。

private static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, CSSDirection parentDirection) {

        for (int i = 0, childCount = node.getChildCount(); i < childCount; i++) {   //重置所有child元素的csslayout结果
            node.getChildAt(i).csslayout.resetResult();
        }
        if (!node.isShow()) {//节点不可见,直接返回
            return;
        }

        /** START_GENERATED **/

        CSSDirection direction = resolveDirection(node, parentDirection);  //布局方向,LTR or RTL
        int mainAxis = resolveAxis(getFlexDirection(node), direction);  //设置主轴方向 (取决于布局方向)
        int crossAxis = getCrossFlexDirection(mainAxis, direction); //设置竖轴方向
        int resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); //根据布局方向,处理过的横向(如果是RTL的话 ROW -> ROW_REVERSE/ROW_REVERSE_)

        // Handle width and height cssstyle attributes
        setDimensionFromStyle(node, mainAxis);  //宽或高的保存
        setDimensionFromStyle(node, crossAxis); //宽或高的保存
        // Set the resolved resolution in the node's csslayout
        node.csslayout.direction = direction;  //布局方向

        // The position is set by the parent, but we need to complete it with a
        // delta composed of the margin and left/top/right/bottom //位置
        node.csslayout.position[leading[mainAxis]] += node.cssstyle.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
                getRelativePosition(node, mainAxis);
        node.csslayout.position[trailing[mainAxis]] += node.cssstyle.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) +
                getRelativePosition(node, mainAxis);
        node.csslayout.position[leading[crossAxis]] += node.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
                getRelativePosition(node, crossAxis);
        node.csslayout.position[trailing[crossAxis]] += node.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) +
                getRelativePosition(node, crossAxis);

        // Inline immutable values from the target node to avoid excessive method
        // invocations during the csslayout calculation.
        int childCount = node.getChildCount();//子View的数目
        float paddingAndBorderAxisResolvedRow = ((node.cssstyle.padding.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) +
                node.cssstyle.border.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis])) +
                (node.cssstyle.padding.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]) +
                        node.cssstyle.border.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])));  //横向的border + padding

        if (isMeasureDefined(node)) { //自定义计算方法不为空。
            boolean isResolvedRowDimDefined = !Float.isNaN(node.csslayout.dimensions[dim[resolvedRowAxis]]); //横向的

            float width = CSSConstants.UNDEFINED;
            if ((!Float.isNaN(node.cssstyle.dimensions[dim[resolvedRowAxis]]) && node.cssstyle.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
                width = node.cssstyle.dimensions[DIMENSION_WIDTH];
            } else if (isResolvedRowDimDefined) {
                width = node.csslayout.dimensions[dim[resolvedRowAxis]];
            } else {
                width = parentMaxWidth -
                        (node.cssstyle.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.cssstyle.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]));
            }
            width -= paddingAndBorderAxisResolvedRow;

            // We only need to give a dimension for the text if we haven't got any
            // for it computed yet. It can either be from the cssstyle attribute or because
            // the element is flexible.
            boolean isRowUndefined = !(!Float.isNaN(node.cssstyle.dimensions[dim[resolvedRowAxis]]) && node.cssstyle.dimensions[dim[resolvedRowAxis]] >= 0.0) && !isResolvedRowDimDefined;
            boolean isColumnUndefined = !(!Float.isNaN(node.cssstyle.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.cssstyle.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) &&
                    Float.isNaN(node.csslayout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);

            // Let's not measure the text if we already know both dimensions
            if (isRowUndefined || isColumnUndefined) { //宽 或 高不确定
                MeasureOutput measureDim = node.measure(  //使用自定义的measure方法

                        layoutContext.measureOutput,
                        width
                );
                if (isRowUndefined) { //宽不确定的话,那么宽度= 计算的宽度+padding border
                    node.csslayout.dimensions[DIMENSION_WIDTH] = measureDim.width +
                            paddingAndBorderAxisResolvedRow;
                }
                if (isColumnUndefined) {// node.csslayout.dimensions[DIMENSION_HEIGHT] = measureDim.height +
                            ((node.cssstyle.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.cssstyle.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.cssstyle.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.cssstyle.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
                }
            }
            if (childCount == 0) { //没有child的话,计算完毕,直接返回。
                return;
            }
        }

        boolean isNodeFlexWrap = (node.cssstyle.flexWrap == CSSWrap.WRAP); //是否是FlexWrap

        CSSJustify justifyContent = node.cssstyle.justifyContent; 

        float leadingPaddingAndBorderMain = (node.cssstyle.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.cssstyle.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]));
        float leadingPaddingAndBorderCross = (node.cssstyle.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.cssstyle.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]));
        float paddingAndBorderAxisMain = ((node.cssstyle.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.cssstyle.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.cssstyle.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.cssstyle.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
        float paddingAndBorderAxisCross = ((node.cssstyle.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.cssstyle.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.cssstyle.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.cssstyle.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));

        boolean isMainDimDefined = !Float.isNaN(node.csslayout.dimensions[dim[mainAxis]]); //主轴方向的尺寸是确定的
        boolean isCrossDimDefined = !Float.isNaN(node.csslayout.dimensions[dim[crossAxis]]); //侧轴方向的尺寸是确定的
        boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE); //主轴是横向的

        int i;
        int ii;
        CSSNode child;
        int axis;

        CSSNode firstAbsoluteChild = null; //保存的absolute child
        CSSNode currentAbsoluteChild = null;

        float definedMainDim = CSSConstants.UNDEFINED;
        if (isMainDimDefined) {
            definedMainDim = node.csslayout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; //主轴方向的内容的dimension
        }

        // We want to execute the next two loops one per line with flex-wrap
        int startLine = 0; 
        int endLine = 0;
        // int nextOffset = 0;
        int alreadyComputedNextLayout = 0;
        // We aggregate the total dimensions of the container in those two variables
        float linesCrossDim = 0; //如果有多行的话,这是每一行侧轴方向的尺寸
        float linesMainDim = 0;//如果有多行的话,这是每一行侧轴方向的尺寸        int linesCount = 0; //行数
        while (endLine < childCount) {  //结束的行数 少于 子元素个数的时候。
            // <Loop A> Layout non flexible children and count children by type  : 布局非Flex的children & 分类计数。

            // mainContentDim is accumulation of the dimensions and margin of all the
            // non flexible children. This will be used in order to either set the
            // dimensions of the node if none already exist, or to compute the
            // remaining space left for the flexible children.
            float mainContentDim = 0; //所有非Flexible Children 的尺寸+margin 之和。

            // There are three kind of children, non flexible, flexible and absolute.
            // We need to know how many there are in order to distribute the space.
            int flexibleChildrenCount = 0;
            float totalFlexible = 0; //总体的权重 weight
            int nonFlexibleChildrenCount = 0;

            // Use the line loop to position children in the main axis for as long
            // as they are using a simple stacking behaviour. Children that are
            // immediately stacked in the initial loop will not be touched again
            // in <Loop C>.
            boolean isSimpleStackMain =
                    (isMainDimDefined && justifyContent == CSSJustify.FLEX_START) ||
                            (!isMainDimDefined && justifyContent != CSSJustify.CENTER); //主轴是否是简单的堆叠
            int firstComplexMain = (isSimpleStackMain ? childCount : startLine); //第一个导致复杂布局的子元素

            // Use the initial line loop to position children in the cross axis for
            // as long as they are relatively positioned with alignment STRETCH or
            // FLEX_START. Children that are immediately stacked in the initial loop
            // will not be touched again in <Loop D>.
            boolean isSimpleStackCross = true; //侧轴方向
            int firstComplexCross = childCount;

            CSSNode firstFlexChild = null;
            CSSNode currentFlexChild = null;

            float mainDim = leadingPaddingAndBorderMain; //主轴头部的padding + border
            float crossDim = 0;

            float maxWidth;
            for (i = startLine; i < childCount; ++i) {  // Loop A : 满足一定条件、确定性的child的宽高、位置会被确定。、统计各个类型child的数目
                child = node.getChildAt(i);
                if (!child.isShow()) { //不可见,直接下一个。
                    endLine = i + 1;
                    continue;
                }
                child.lineIndex = linesCount; //行数,记录在child上,下面有用。

                child.nextAbsoluteChild = null; //组成链式储存结构
                child.nextFlexChild = null;

                CSSAlign alignItem = getAlignItem(node, child);  //Child的侧轴对齐方式

                // Pre-fill cross axis dimensions when the child is using stretch before
                // we call the recursive csslayout pass
                if (alignItem == CSSAlign.STRETCH &&  //Child的侧轴方向的宽度为空或者小于0,alignitem属性是stretch,位置是相对定位,parent的侧轴尺寸确定  -> 侧轴方向的尺寸可确定
                        child.cssstyle.positionType == CSSPositionType.RELATIVE &&
                        isCrossDimDefined &&
                        !(!Float.isNaN(child.cssstyle.dimensions[dim[crossAxis]]) && child.cssstyle.dimensions[dim[crossAxis]] >= 0.0)) {
                    child.csslayout.dimensions[dim[crossAxis]] = Math.max(
                            boundAxis(child, crossAxis, node.csslayout.dimensions[dim[crossAxis]] -
                                    paddingAndBorderAxisCross - (child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
                            // You never want to go smaller than padding + border
                            ((child.cssstyle.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.cssstyle.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.cssstyle.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
                    );
                } else if (child.cssstyle.positionType == CSSPositionType.ABSOLUTE) {  //绝对布局,保存下来,这个布局比较easy。
                    // Store a private linked list of absolutely positioned children
                    // so that we can efficiently traverse them later.
                    if (firstAbsoluteChild == null) {
                        firstAbsoluteChild = child;
                    }
                    if (currentAbsoluteChild != null) {
                        currentAbsoluteChild.nextAbsoluteChild = child;
                    }
                    currentAbsoluteChild = child;    //保存起来绝对布局的元素,以便有效的重用。

                    // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
                    // left and right or top and bottom).
                    for (ii = 0; ii < 2; ii++) {
                        axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
                        if (!Float.isNaN(node.csslayout.dimensions[dim[axis]]) &&  // 主轴不为空、child节点主轴的尺寸不确定、child节点的position属性不为空。 -> Child节点的尺寸
                                !(!Float.isNaN(child.cssstyle.dimensions[dim[axis]]) && child.cssstyle.dimensions[dim[axis]] >= 0.0) &&
                                !Float.isNaN(child.cssstyle.position[leading[axis]]) &&
                                !Float.isNaN(child.cssstyle.position[trailing[axis]])) {
                            child.csslayout.dimensions[dim[axis]] = Math.max( //parent对应轴的尺寸- padding-maigin-border-position
                                    boundAxis(child, axis, node.csslayout.dimensions[dim[axis]] -
                                            ((node.cssstyle.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + node.cssstyle.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (node.cssstyle.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.cssstyle.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) -
                                            (child.cssstyle.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
                                            (Float.isNaN(child.cssstyle.position[leading[axis]]) ? 0 : child.cssstyle.position[leading[axis]]) -
                                            (Float.isNaN(child.cssstyle.position[trailing[axis]]) ? 0 : child.cssstyle.position[trailing[axis]])),
                                    // You never want to go smaller than padding+border
                                    ((child.cssstyle.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + child.cssstyle.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (child.cssstyle.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + child.cssstyle.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
                            );
                        }
                    }
                }

                float nextContentDim = 0; //下一个内容的尺寸

                // It only makes sense to consider a child flexible if we have a computed
                // dimension for the node.
                if (isMainDimDefined && (child.cssstyle.positionType == CSSPositionType.RELATIVE && child.cssstyle.flex > 0)) { // Flexible Children
                    flexibleChildrenCount++;
                    totalFlexible += child.cssstyle.flex;

                    // Store a private linked list of flexible children so that we can
                    // efficiently traverse them later.
                    if (firstFlexChild == null) {
                        firstFlexChild = child;
                    }
                    if (currentFlexChild != null) {
                        currentFlexChild.nextFlexChild = child;
                    }
                    currentFlexChild = child;  //保存FlexChild链表

                    // Even if we don't know its exact size yet, we already know the padding,
                    // border and margin. We'll use this partial information, which represents
                    // the smallest possible size for the child, to compute the remaining
                    // available space.
                    nextContentDim = ((child.cssstyle.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.cssstyle.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.cssstyle.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.cssstyle.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) +
                            (child.cssstyle.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));

                } else {
                    maxWidth = CSSConstants.UNDEFINED;
                    if (!isMainRowDirection) { //不是横向的时候,算出来最大宽度
                        if ((!Float.isNaN(node.cssstyle.dimensions[dim[resolvedRowAxis]]) && node.cssstyle.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
                            maxWidth = node.csslayout.dimensions[dim[resolvedRowAxis]] -
                                    paddingAndBorderAxisResolvedRow;
                        } else {
                            maxWidth = parentMaxWidth -
                                    (node.cssstyle.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.cssstyle.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
                                    paddingAndBorderAxisResolvedRow;
                        }
                    }

                    // This is the main recursive call. We csslayout non flexible children.
                    if (alreadyComputedNextLayout == 0) {
                        layoutNode(layoutContext, child, maxWidth, direction); // 对于非Flexible的Children、非换行情况下,递归调用。
                    }

                    // Absolute positioned elements do not take part of the csslayout, so we
                    // don't use them to compute mainContentDim
                    if (child.cssstyle.positionType == CSSPositionType.RELATIVE) { /绝对布局不参与计算,不需要累计绝对布局的尺寸到mainContentDim上。
                        nonFlexibleChildrenCount++;
                        // At this point we know the final size and margin of the element.
                        nextContentDim = (child.csslayout.dimensions[dim[mainAxis]] + child.cssstyle.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
                    }
                }

                // The element we are about to add would make us go to the next line
                if (isNodeFlexWrap &&  //节点的布局是FlexWrap
                        isMainDimDefined && //主轴尺寸确定
                        mainContentDim + nextContentDim > definedMainDim && //主要当前内容尺寸+下一个内容的尺寸 大于 主轴的最大内容
                        // If there's only one element, then it's bigger than the content
                        // and needs its own line
                        i != startLine) { //同时Children不只一个。 那么换行。
                    nonFlexibleChildrenCount--;
                    alreadyComputedNextLayout = 1;
                    break;   //跳出Loop A
                }

                // Disable simple stacking in the main axis for the current line as
                // we found a non-trivial child. The remaining children will be laid out
                // in <Loop C>.
                if (isSimpleStackMain &&
                        (child.cssstyle.positionType != CSSPositionType.RELATIVE || (child.cssstyle.positionType == CSSPositionType.RELATIVE && child.cssstyle.flex > 0))) {
                    isSimpleStackMain = false;
                    firstComplexMain = i;
                }

                // Disable simple stacking in the cross axis for the current line as
                // we found a non-trivial child. The remaining children will be laid out
                // in <Loop D>.
                if (isSimpleStackCross &&
                        (child.cssstyle.positionType != CSSPositionType.RELATIVE ||
                                (alignItem != CSSAlign.STRETCH && alignItem != CSSAlign.FLEX_START) ||
                                Float.isNaN(child.csslayout.dimensions[dim[crossAxis]]))) {
                    isSimpleStackCross = false;
                    firstComplexCross = i;
                }

                if (isSimpleStackMain) { //主轴上简单堆叠
                    child.csslayout.position[pos[mainAxis]] += mainDim;
                    if (isMainDimDefined) {
                        child.csslayout.position[trailing[mainAxis]] = node.csslayout.dimensions[dim[mainAxis]] - child.csslayout.dimensions[dim[mainAxis]] - child.csslayout.position[pos[mainAxis]];
                    }

                    mainDim += (child.csslayout.dimensions[dim[mainAxis]] + child.cssstyle.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
                    crossDim = Math.max(crossDim, boundAxis(child, crossAxis, (child.csslayout.dimensions[dim[crossAxis]] + child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
                }

                if (isSimpleStackCross) { //侧轴上简单堆叠
                    child.csslayout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
                    if (isCrossDimDefined) {
                        child.csslayout.position[trailing[crossAxis]] = node.csslayout.dimensions[dim[crossAxis]] - child.csslayout.dimensions[dim[crossAxis]] - child.csslayout.position[pos[crossAxis]];
                    }
                }

                alreadyComputedNextLayout = 0;
                mainContentDim += nextContentDim;  //当前行的主内容尺寸
                endLine = i + 1;  //下一行
            }

            // <Loop B> Layout flexible children and allocate empty space  // 确定flexible children的尺寸 以及 分配剩余空间。

            // In order to position the elements in the main axis, we have two
            // controls. The space between the beginning and the first element
            // and the space between each two elements.
            float leadingMainDim = 0;  //首部空余空间
            float betweenMainDim = 0; //元素之前空余空间。

            // The remaining available space that needs to be allocated
            float remainingMainDim = 0;
            if (isMainDimDefined) { //最大宽度确定的情况下。
                remainingMainDim = definedMainDim - mainContentDim;
            } else {
                remainingMainDim = Math.max(mainContentDim, 0) - mainContentDim;
            }

            // If there are flexible children in the mix, they are going to fill the
            // remaining space
            if (flexibleChildrenCount != 0) { //有Children
                float flexibleMainDim = remainingMainDim / totalFlexible;
                float baseMainDim;  //计算出来的空间
                float boundMainDim; //根据max/min  height/width 限制下来的空间。

                // If the flex share of remaining space doesn't meet min/max bounds,
                // remove this child from flex calculations.
                currentFlexChild = firstFlexChild;
                while (currentFlexChild != null) { //遍历flexchildren链表
                    if (currentFlexChild.isShow()) { //当前Flex Child可见
                        baseMainDim = flexibleMainDim * currentFlexChild.cssstyle.flex +
                                ((currentFlexChild.cssstyle.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.cssstyle.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.cssstyle.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.cssstyle.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
                        boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);

                        if (baseMainDim != boundMainDim) { //两者不等于的话,意味着:child真正占据的空间和Flex比例对不上了,需要重新计算flexibleMainDim。
                            remainingMainDim -= boundMainDim;
                            totalFlexible -= currentFlexChild.cssstyle.flex;
                        }
                    }
                    currentFlexChild = currentFlexChild.nextFlexChild;
                }
                flexibleMainDim = remainingMainDim / totalFlexible;  //如果触达了最大值,那么新计算出来的flexibleMainDim比以前的大;反之更小。

                // The non flexible children can overflow the container, in this case
                // we should just assume that there is no space available.
                if (flexibleMainDim < 0) {
                    flexibleMainDim = 0;
                }

                currentFlexChild = firstFlexChild;
                while (currentFlexChild != null) { //再次遍历
                    if (currentFlexChild.isShow()) {
                        // At this point we know the final size of the element in the main
                        // dimension  //根据Flex计算出主轴方向的dimension
                        currentFlexChild.csslayout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
                                flexibleMainDim * currentFlexChild.cssstyle.flex +
                                        ((currentFlexChild.cssstyle.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.cssstyle.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.cssstyle.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.cssstyle.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))
                        );

                        maxWidth = CSSConstants.UNDEFINED;
                        if ((!Float.isNaN(node.cssstyle.dimensions[dim[resolvedRowAxis]]) && node.cssstyle.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
                            maxWidth = node.csslayout.dimensions[dim[resolvedRowAxis]] -
                                    paddingAndBorderAxisResolvedRow;
                        } else if (!isMainRowDirection) {
                            maxWidth = parentMaxWidth -
                                    (node.cssstyle.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.cssstyle.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
                                    paddingAndBorderAxisResolvedRow;
                        }
                          //递归调用
                        // And we recursively call the csslayout algorithm for this child
                        layoutNode(layoutContext, currentFlexChild, maxWidth, direction);
                    }
                    child = currentFlexChild;
                    currentFlexChild = currentFlexChild.nextFlexChild;
                    child.nextFlexChild = null;
                }

                // We use justifyContent to figure out how to allocate the remaining
                // space available
            } else if (justifyContent != CSSJustify.FLEX_START) {
                if (justifyContent == CSSJustify.CENTER) {
                    leadingMainDim = remainingMainDim / 2;
                } else if (justifyContent == CSSJustify.FLEX_END) {
                    leadingMainDim = remainingMainDim;
                } else if (justifyContent == CSSJustify.SPACE_BETWEEN) {
                    remainingMainDim = Math.max(remainingMainDim, 0);
                    if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
                        betweenMainDim = remainingMainDim /
                                (flexibleChildrenCount + nonFlexibleChildrenCount - 1);
                    } else {
                        betweenMainDim = 0;
                    }
                } else if (justifyContent == CSSJustify.SPACE_AROUND) {
                    // Space on the edges is half of the space between elements
                    betweenMainDim = remainingMainDim /
                            (flexibleChildrenCount + nonFlexibleChildrenCount);
                    leadingMainDim = betweenMainDim / 2;
                }
            }

            // <Loop C> Position elements in the main axis and compute dimensions //计算主轴方向Children的位置
            
            // At this point, all the children have their dimensions set. We need to
            // find their position. In order to do that, we accumulate data in
            // variables that are also useful to compute the total dimensions of the
            // container!
            mainDim += leadingMainDim; //主轴方向 已占用的 dimension

            for (i = firstComplexMain; i < endLine; ++i) { //第一个没法确定位置的元素开始
                child = node.getChildAt(i);
                if (!child.isShow()) {
                    continue;
                }

                if (child.cssstyle.positionType == CSSPositionType.ABSOLUTE &&
                        !Float.isNaN(child.cssstyle.position[leading[mainAxis]])) { // 绝对布局+margin不为空。
                    // In case the child is position absolute and has left/top being
                    // defined, we override the position to whatever the user said
                    // (and margin/border).
                    child.csslayout.position[pos[mainAxis]] = (Float.isNaN(child.cssstyle.position[leading[mainAxis]]) ? 0 : child.cssstyle.position[leading[mainAxis]]) +
                            node.cssstyle.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
                            child.cssstyle.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]);
                } else {
                    // If the child is position absolute (without top/left) or relative,
                    // we put it at the current accumulated offset.
                    child.csslayout.position[pos[mainAxis]] += mainDim; //否则加上当前的pos值

                    // Define the trailing position accordingly.
                    if (isMainDimDefined) { //算出尾部的位置,相当于margin bottom
                        child.csslayout.position[trailing[mainAxis]] = node.csslayout.dimensions[dim[mainAxis]] - child.csslayout.dimensions[dim[mainAxis]] - child.csslayout.position[pos[mainAxis]];
                    }

                    // Now that we placed the element, we need to update the variables
                    // We only need to do that for relative elements. Absolute elements
                    // do not take part in that phase.
                    if (child.cssstyle.positionType == CSSPositionType.RELATIVE) { //相对布局的时候,计算mainDim和crossDim
                        // The main dimension is the sum of all the elements dimension plus
                        // the spacing.
                        mainDim += betweenMainDim + (child.csslayout.dimensions[dim[mainAxis]] + child.cssstyle.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
                        // The cross dimension is the max of the elements dimension since there
                        // can only be one element in that cross dimension.
                        crossDim = Math.max(crossDim, boundAxis(child, crossAxis, (child.csslayout.dimensions[dim[crossAxis]] + child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
                    }
                }
            }

            float containerCrossAxis = node.csslayout.dimensions[dim[crossAxis]];
            if (!isCrossDimDefined) { //容器侧轴的尺寸不确定
                containerCrossAxis = Math.max( 
                        // For the cross dim, we add both sides at the end because the value
                        // is aggregate via a max function. Intermediate negative values
                        // can mess this computation otherwise
                        boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
                        paddingAndBorderAxisCross
                );
            }

            // <Loop D> Position elements in the cross axis //计算侧轴方向元素的位置。
            for (i = firstComplexCross; i < endLine; ++i) { //第一个无法确定位置的侧轴元素坐标
                child = node.getChildAt(i);
                if (!child.isShow()) {
                    continue;
                }

                if (child.cssstyle.positionType == CSSPositionType.ABSOLUTE &&
                        !Float.isNaN(child.cssstyle.position[leading[crossAxis]])) { //绝对布局的时候,直接加上去。
                    // In case the child is absolutely positionned and has a
                    // top/left/bottom/right being set, we override all the previously
                    // computed positions to set it correctly.
                    child.csslayout.position[pos[crossAxis]] = (Float.isNaN(child.cssstyle.position[leading[crossAxis]]) ? 0 : child.cssstyle.position[leading[crossAxis]]) +
                            node.cssstyle.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
                            child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);

                } else {
                    float leadingCrossDim = leadingPaddingAndBorderCross;  //侧轴头部内容(侧轴node节点的padding + border)

                    // For a relative children, we're either using alignItems (parent) or
                    // alignSelf (child) in order to determine the position in the cross axis
                    if (child.cssstyle.positionType == CSSPositionType.RELATIVE) { //相对布局
            /*eslint-disable */
                        // This variable is intentionally re-defined as the code is transpiled to a block scope language
                        CSSAlign alignItem = getAlignItem(node, child);
            /*eslint-enable */
                        if (alignItem == CSSAlign.STRETCH) { //撑满
                            // You can only stretch if the dimension has not already been set
                            // previously.
                            if (Float.isNaN(child.csslayout.dimensions[dim[crossAxis]])) {
                                child.csslayout.dimensions[dim[crossAxis]] = Math.max(
                                        boundAxis(child, crossAxis, containerCrossAxis -
                                                paddingAndBorderAxisCross - (child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
                                        // You never want to go smaller than padding
                                        ((child.cssstyle.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.cssstyle.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.cssstyle.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
                                );
                            }
                        } else if (alignItem != CSSAlign.FLEX_START) { 
                            // The remaining space between the parent dimensions+padding and child
                            // dimensions+margin.
                            float remainingCrossDim = containerCrossAxis -
                                    paddingAndBorderAxisCross - (child.csslayout.dimensions[dim[crossAxis]] + child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]));

                            if (alignItem == CSSAlign.CENTER) {
                                leadingCrossDim += remainingCrossDim / 2;
                            } else { // CSSAlign.FLEX_END
                                leadingCrossDim += remainingCrossDim;
                            }
                        }
                    }

                    // And we apply the position
                    child.csslayout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;

                    // Define the trailing position accordingly.
                    if (isCrossDimDefined) {  //侧轴固定的话,还可以自己算出尾部的坐标。
                        child.csslayout.position[trailing[crossAxis]] = node.csslayout.dimensions[dim[crossAxis]] - child.csslayout.dimensions[dim[crossAxis]] - child.csslayout.position[pos[crossAxis]];
                    }
                }
            }

            linesCrossDim += crossDim; //侧轴行尺寸(累计)
            linesMainDim = Math.max(linesMainDim, mainDim); //主轴行的尺寸。(取最大值)
            linesCount += 1; // 行数
            startLine = endLine; //开始的child index ,开始换行。
        }

        //前四轮循环下来,所有的children的位置和宽高都确定了。

        // <Loop E> : 超过一行的情况下,需要计算侧轴方向的位置和大小(根据alignItem和alignSelf属性)
        //
        // Note(prenaux): More than one line, we need to csslayout the crossAxis
        // according to alignContent.
        //
        // Note that we could probably remove <Loop D> and handle the one line case
        // here too, but for the moment this is safer since it won't interfere with
        // previously working code.(实际上可以移除loop D,但是loop d之前跑的OK,就不干预了……)
        //
        // See specs:
        // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#csslayout-algorithm
        // section 9.4
        //
        if (linesCount > 1 && isCrossDimDefined) { //超过一行,并且侧轴内容确定的
            float nodeCrossAxisInnerSize = node.csslayout.dimensions[dim[crossAxis]] - paddingAndBorderAxisCross;  //侧轴内容的size
            float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; //剩余空间的尺寸

            float crossDimLead = 0;
            float currentLead = leadingPaddingAndBorderCross; //头部的距离()

            CSSAlign alignContent = node.cssstyle.alignContent;
            if (alignContent == CSSAlign.FLEX_END) { //尾部对齐的话,剩余空间全部加到首部距离里面
                currentLead += remainingAlignContentDim;
            } else if (alignContent == CSSAlign.CENTER) { //居中的话,加一半
                currentLead += remainingAlignContentDim / 2;
            } else if (alignContent == CSSAlign.STRETCH) { //撑满的话,
                if (nodeCrossAxisInnerSize > linesCrossDim) {//如果内容尺寸大于所有行的尺寸,即剩余空间尺寸大于0
                    crossDimLead = (remainingAlignContentDim / linesCount); //按比例计算。
                }
            }

            int endIndex = 0;
            for (i = 0; i < linesCount; ++i) { //遍历所有的行
                int startIndex = endIndex;

                // compute the line's height and find the endIndex
                float lineHeight = 0;
                for (ii = startIndex; ii < childCount; ++ii) { //遍历每一行中的元素,计算出本行的行高
                    child = node.getChildAt(ii);

                    if (!child.isShow() || child.cssstyle.positionType != CSSPositionType.RELATIVE) {
                        continue;
                    }
                    if (child.lineIndex != i) {  //下一行,跳出。
                        break;
                    }
                    if (!Float.isNaN(child.csslayout.dimensions[dim[crossAxis]])) { 
                        lineHeight = Math.max(
                                lineHeight,
                                child.csslayout.dimensions[dim[crossAxis]] + (child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))
                        );
                    }
                }
                endIndex = ii;
                lineHeight += crossDimLead; //修正行高

                for (ii = startIndex; ii < endIndex; ++ii) { //根据计算出来的新的行高,重新确定位置。
                    child = node.getChildAt(ii);

                    if (!child.isShow() || child.cssstyle.positionType != CSSPositionType.RELATIVE) {
                        continue;
                    }

                    CSSAlign alignContentAlignItem = getAlignItem(node, child);
                    if (alignContentAlignItem == CSSAlign.FLEX_START) {
                        child.csslayout.position[pos[crossAxis]] = currentLead + child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
                    } else if (alignContentAlignItem == CSSAlign.FLEX_END) {
                        child.csslayout.position[pos[crossAxis]] = currentLead + lineHeight - child.cssstyle.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.csslayout.dimensions[dim[crossAxis]];
                    } else if (alignContentAlignItem == CSSAlign.CENTER) {
                        float childHeight = child.csslayout.dimensions[dim[crossAxis]];
                        child.csslayout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
                    } else if (alignContentAlignItem == CSSAlign.STRETCH) {
                        child.csslayout.position[pos[crossAxis]] = currentLead + child.cssstyle.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
                        // TODO(prenaux): Correctly set the height of items with undefined
                        //                (auto) crossAxis dimension.
                    }
                }

                currentLead += lineHeight;
            }
        }

        boolean needsMainTrailingPos = false;
        boolean needsCrossTrailingPos = false;

        // If the user didn't specify a width or height, and it has not been set
        // by the container, then we set it via the children.
        if (!isMainDimDefined) { //主轴不确定的话,尝试用主轴方向行的内容加padding、border计算。
            node.csslayout.dimensions[dim[mainAxis]] = Math.max(
                    // We're missing the last padding at this point to get the final
                    // dimension
                    boundAxis(node, mainAxis, linesMainDim + (node.cssstyle.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.cssstyle.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))),
                    // We can never assign a width smaller than the padding and borders
                    paddingAndBorderAxisMain
            );

            if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
                    mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { //如果是反向布局的话,需要知道尾部位置。
                needsMainTrailingPos = true;
            }
        }

        if (!isCrossDimDefined) { //侧轴不固定
            node.csslayout.dimensions[dim[crossAxis]] = Math.max(
                    // For the cross dim, we add both sides at the end because the value
                    // is aggregate via a max function. Intermediate negative values
                    // can mess this computation otherwise
                    boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
                    paddingAndBorderAxisCross
            );

            if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
                    crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
                needsCrossTrailingPos = true;
            }
        }

        // <Loop F> Set trailing position if necessary
        if (needsMainTrailingPos || needsCrossTrailingPos) { //如果是反向布局的话,这两个需要确定。
            for (i = 0; i < childCount; ++i) {
                child = node.getChildAt(i);
                if (!child.isShow()) {
                    continue;
                }

                if (needsMainTrailingPos) { //根据node dimension和 child dimension 以及 child position计算
                    child.csslayout.position[trailing[mainAxis]] = node.csslayout.dimensions[dim[mainAxis]] - child.csslayout.dimensions[dim[mainAxis]] - child.csslayout.position[pos[mainAxis]];
                }

                if (needsCrossTrailingPos) {
                    child.csslayout.position[trailing[crossAxis]] = node.csslayout.dimensions[dim[crossAxis]] - child.csslayout.dimensions[dim[crossAxis]] - child.csslayout.position[pos[crossAxis]];
                }
            }
        }

        // <Loop G> Calculate dimensions for absolutely positioned elements  //最后计算绝对布局的尺寸。
        currentAbsoluteChild = firstAbsoluteChild;
        while (currentAbsoluteChild != null) {
            if (currentAbsoluteChild.isShow()) {
                // Pre-fill dimensions when using absolute position and both offsets for
                // the axis are defined (either both left and right or top and bottom).
                for (ii = 0; ii < 2; ii++) {
                    axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;

                    if (!Float.isNaN(node.csslayout.dimensions[dim[axis]]) &&
                            !(!Float.isNaN(currentAbsoluteChild.cssstyle.dimensions[dim[axis]]) && currentAbsoluteChild.cssstyle.dimensions[dim[axis]] >= 0.0) &&
                            !Float.isNaN(currentAbsoluteChild.cssstyle.position[leading[axis]]) &&
                            !Float.isNaN(currentAbsoluteChild.cssstyle.position[trailing[axis]])) {
                        currentAbsoluteChild.csslayout.dimensions[dim[axis]] = Math.max(
                                boundAxis(currentAbsoluteChild, axis, node.csslayout.dimensions[dim[axis]] -
                                        (node.cssstyle.border.getWithFallback(leadingSpacing[axis], leading[axis]) + node.cssstyle.border.getWithFallback(trailingSpacing[axis], trailing[axis])) -
                                        (currentAbsoluteChild.cssstyle.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.cssstyle.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
                                        (Float.isNaN(currentAbsoluteChild.cssstyle.position[leading[axis]]) ? 0 : currentAbsoluteChild.cssstyle.position[leading[axis]]) -
                                        (Float.isNaN(currentAbsoluteChild.cssstyle.position[trailing[axis]]) ? 0 : currentAbsoluteChild.cssstyle.position[trailing[axis]])
                                ),
                                // You never want to go smaller than padding
                                ((currentAbsoluteChild.cssstyle.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.cssstyle.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (currentAbsoluteChild.cssstyle.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + currentAbsoluteChild.cssstyle.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
                        );
                    }

                    if (!Float.isNaN(currentAbsoluteChild.cssstyle.position[trailing[axis]]) &&
                            !!Float.isNaN(currentAbsoluteChild.cssstyle.position[leading[axis]])) {
                        currentAbsoluteChild.csslayout.position[leading[axis]] =
                                node.csslayout.dimensions[dim[axis]] -
                                        currentAbsoluteChild.csslayout.dimensions[dim[axis]] -
                                        (Float.isNaN(currentAbsoluteChild.cssstyle.position[trailing[axis]]) ? 0 : currentAbsoluteChild.cssstyle.position[trailing[axis]]);
                    }
                }
            }
            child = currentAbsoluteChild;
            currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
            child.nextAbsoluteChild = null;
        }
    }

方法太长,基本流程就是我上面列举的,重要的看注释,会有对应流程的分析。

主要的注意的点是

A-D循环,每次计算的是一行。如果发生了换行,那么重新要run一次。


4 总结

 布局代码看起来挺长、复杂,其实自信分析下来,除了几个比较关键要理解的点外,总体过程也不难,

另外中间的英文注释很重要。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值