LinearLayout源码解析

本文深入剖析了Android中的LinearLayout布局,包括其源码实现、性能特点及如何高效利用。特别关注了onMeasure()方法,介绍了如何通过调整参数实现不同效果,如设置分割线、控制子视图对齐方式等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LinearLayout是安卓开发中最常用布局之一,此文依托分析其源码,了解其性能特色,目标使以后开发使用它更得心应手

/**
 * LinearLayout是安卓开发中最常用布局之一,此文依托分析其源码,了解其性能特色,目标使以后开发使用它更得心应手
 * 重点查看onMeasure()
 * 默认方向是横向
 */

public class LinearLayout extends ViewGroup {

    public static final int HORIZONTAL = 0;
    public static final int VERTICAL = 1;

    //divider的几种类型
     public static final int SHOW_DIVIDER_NONE = 0;
     public static final int SHOW_DIVIDER_BEGINNING = 1;
     public static final int SHOW_DIVIDER_MIDDLE = 2;
     public static final int SHOW_DIVIDER_END = 4;

    private boolean mBaselineAligned = true;//默认是 子view按Baseline对齐

    //默认为0,当前LinearLayout作为其他layout的子View时,以该LinearLayout中哪个TextView(Butt//on、EditView)的基准线为对外基准线
    private int mBaselineAlignedChildIndex = -1;

    private int mBaselineChildTop = 0;
    
    private int mOrientation;

    private int mGravity = Gravity.START | Gravity.TOP;

    private int mTotalLength;//测量的所有子view总高度

    private float mWeightSum;//权重总和

    //是否用最大个的子view,如果用的话,所有可见的子view都跟这个一样大
    private boolean mUseLargestChild;

    private Drawable mDivider;//分割线的Drawable,会在重写的onDraw()中绘制
    private int mDividerWidth;
    private int mDividerHeight;
    private int mShowDividers;//对应上面divider的几种类型
    private int mDividerPadding;

    private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;//RTL相关
    
    //最终调用的构造方法
    public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        //构造方法中 各个参数的初始化
        final TypedArray a = ...
    }

  
    public void setShowDividers(@DividerMode int showDividers) {
        if (showDividers != mShowDividers) {//如果这个类型发生改变
            requestLayout();//重新布局
        }
        mShowDividers = showDividers;
    }

    public void setDividerDrawable(Drawable divider) {
        if (divider == mDivider) {
            return;
        }
        mDivider = divider;
        if (divider != null) {
            mDividerWidth = divider.getIntrinsicWidth();//获取DividerWidth自己的宽高
            mDividerHeight = divider.getIntrinsicHeight();
        } else {
            mDividerWidth = 0;
            mDividerHeight = 0;
        }
        setWillNotDraw(divider == null);//如果没有divider就添加一个不需要draw的flag
        requestLayout();
    }

   
    public void setDividerPadding(int padding) {
        mDividerPadding = padding;
    }

    //此方法会根据自身布局方向,分开绘制,内部逻辑类似
    @Override
    protected void onDraw(Canvas canvas) {
        if (mDivider == null) {//默认divider是宽高为0的
            return;
        }
        if (mOrientation == VERTICAL) {
            drawDividersVertical(canvas);//竖向布局绘制divider
        } else {
            drawDividersHorizontal(canvas);//横向~
        }
    }

    //竖向布局绘制divider
    void drawDividersVertical(Canvas canvas) {
        final int count = getVirtualChildCount();//获取子的个数
        for (int i = 0; i < count; i++) {
            final View child = getVirtualChildAt(i);

            if (child != null && child.getVisibility() != GONE) {
                if (hasDividerBeforeChildAt(i)) {//此子view可见且在其前有divider
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    final int top = child.getTop() - lp.topMargin - mDividerHeight;//计算得到divider的top位置
                    drawHorizontalDivider(canvas, top);//draw横向的divider(整体布局是竖向)
                }
            }
        }

        if (hasDividerBeforeChildAt(count)) {//计算是否需要在尾部绘制divider
            final View child = getLastNonGoneChild();//最后一个没有gone的子view
            int bottom = 0;
            if (child == null) {
                bottom = getHeight() - getPaddingBottom() - mDividerHeight;//高度-底部内边距-divider高度
            } else {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                bottom = child.getBottom() + lp.bottomMargin;//最后一个view到父布局顶部距离加上其到底部的外边距
            }
            drawHorizontalDivider(canvas, bottom);//普通的通过计算Rect 绘制divider
        }
    }

    //最后一个没有gone的子view
    private View getLastNonGoneChild() {
        for (int i = getVirtualChildCount() - 1; i >= 0; i--) {
            View child = getVirtualChildAt(i);
            if (child != null && child.getVisibility() != GONE) {
                return child;
            }
        }
        return null;
    }

    //横向布局绘制divider,大部分类似于竖向布局
    void drawDividersHorizontal(Canvas canvas) {
        final int count = getVirtualChildCount();
        final boolean isLayoutRtl = isLayoutRtl();//判断是否为RTL布局
        for (int i = 0; i < count; i++) {
            final View child = getVirtualChildAt(i);

            if (child != null && child.getVisibility() != GONE) {
                if (hasDividerBeforeChildAt(i)) {
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    final int position;
                    if (isLayoutRtl) {
                        position = child.getRight() + lp.rightMargin;
                    } else {
                        position = child.getLeft() - lp.leftMargin - mDividerWidth;
                    }
                    drawVerticalDivider(canvas, position);//绘制竖向的divider
                }
            }
        }

        if (hasDividerBeforeChildAt(count)) {
            final View child = getLastNonGoneChild();
            int position;
            if (child == null) {
                if (isLayoutRtl) {
                    position = getPaddingLeft();
                } else {
                    position = getWidth() - getPaddingRight() - mDividerWidth;
                }
            } else {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (isLayoutRtl) {
                    position = child.getLeft() - lp.leftMargin - mDividerWidth;
                } else {
                    position = child.getRight() + lp.rightMargin;
                }
            }
            drawVerticalDivider(canvas, position);
        }
    }

    void drawHorizontalDivider(Canvas canvas, int top) {
        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
        mDivider.draw(canvas);
    }

    void drawVerticalDivider(Canvas canvas, int left) {
        mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
        mDivider.draw(canvas);
    }

    public boolean isMeasureWithLargestChildEnabled() {//是不是依赖最大的字view
        return mUseLargestChild;
    }

    @Override
    public int getBaseline() {//获取ll对外的baseline
        ...
    }
    
    View getVirtualChildAt(int index) {
        return getChildAt(index);
    }

    int getVirtualChildCount() {
        return getChildCount();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

  
    protected boolean hasDividerBeforeChildAt(int childIndex) {
        if (childIndex == getVirtualChildCount()) {
            // 位运算符计算是不是需要在尾部drawDivider
            return (mShowDividers & SHOW_DIVIDER_END) != 0;
        }
        boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex);//判断index前面所有view是不是皆为GONE
        if (allViewsAreGoneBefore) {
            // 这是第一个没有gone掉的子view,且是SHOW_DIVIDER_BEGINNING(invisible时依然会显示)
            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
        } else {// 非第一个子view,且是SHOW_DIVIDER_MIDDLE
            return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
        }
    }

    /**
     * Checks whether all (virtual) child views before the given index are gone.
     */
    private boolean allViewsAreGoneBefore(int childIndex) {
        for (int i = childIndex - 1; i >= 0; i--) {
            View child = getVirtualChildAt(i);
            if (child != null && child.getVisibility() != GONE) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param widthMeasureSpec 父控件传进来的width的要求
     * @param heightMeasureSpec  
     * 分析可以得出:
     * 如果是一个非权重,非useLargest的LinearLayout,只走第一层for循环(测量一次就够)
     * 一个但useLargestChild,高度没定死的LinearLayout会for两次
     * 一个使用权重的布局会for三次
     */
    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
        mTotalLength = 0;
        int maxWidth = 0;//记录最大宽度
        int childState = 0;/经过所有子viewmeasure后的state状态
        int alternativeMaxWidth = 0;//权重布局下计算的MaxWidth
        int weightedMaxWidth = 0;//临时存的每个子view的权重最大宽度
        boolean allFillParent = true;//子view全部是MATCH_PARENT
        float totalWeight = 0;//全部权重

        final int count = getVirtualChildCount();//总数

        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);//宽度模式
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);//高度模式

        boolean matchWidth = false;//至少有一个子view需要测量宽度(为MATCH_PARENT)
        boolean skippedMeasure = false;

        final int baselineChildIndex = mBaselineAlignedChildIndex;
        final boolean useLargestChild = mUseLargestChild;

        int largestChildHeight = Integer.MIN_VALUE;

        // See how tall everyone is. Also remember max width.
        for (int i = 0; i < count; ++i) {//一层for循环
            final View child = getVirtualChildAt(i);

            if (child == null) {
                mTotalLength += measureNullChild(i);
                continue;
            }

            if (child.getVisibility() == View.GONE) {
               i += getChildrenSkipCount(child, i);
               continue;
            }

            if (hasDividerBeforeChildAt(i)) {//如果该子view前有divider
                mTotalLength += mDividerHeight;
            }

            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

            totalWeight += lp.weight;//该子view权重

            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {//确切高度,且height=0 权重>0
                
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                skippedMeasure = true;//后面跳过Measure
            } else {//高度不是确定可能是AT_MOST/UNSPECIFIED
                int oldHeight = Integer.MIN_VALUE;

                if (lp.height == 0 && lp.weight > 0) {
                    // heightMode is either UNSPECIFIED or AT_MOST, and this
                    // child wanted to stretch to fill available space.
                    // Translate that to WRAP_CONTENT so that it does not end up
                    // with a height of 0
                    oldHeight = 0;
                    lp.height = LayoutParams.WRAP_CONTENT;
                }

                // Determine how big this child would like to be. If this or
                // previous children have given a weight, then we allow it to
                // use all available space (and we will shrink things later
                // if needed).
                measureChildBeforeLayout(
                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
                       totalWeight == 0 ? mTotalLength : 0);//调用viewgroup中方法测量子view

                if (oldHeight != Integer.MIN_VALUE) {
                   lp.height = oldHeight;
                }

                final int childHeight = child.getMeasuredHeight();
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +//此子view高度和外边距加到总高度中
                       lp.bottomMargin + getNextLocationOffset(child));
                if (useLargestChild) {
                    largestChildHeight = Math.max(childHeight, largestChildHeight);//记录最大子view高度
                }
            }

            /**
             * If applicable, compute the additional offset to the child's baseline
             * we'll need later when asked {@link #getBaseline}.
             */
            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
               mBaselineChildTop = mTotalLength;//记录baseline位置
            }

            // if we are trying to use a child index for our baseline, the above
            // book keeping only works if there are no children above it with
            // weight.  fail fast to aid the developer.
            if (i < baselineChildIndex && lp.weight > 0) {
                throw new RuntimeException("A child of LinearLayout with index "
                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
                        + "won't work.  Either remove the weight, or don't set "
                        + "mBaselineAlignedChildIndex.");
            }

            boolean matchWidthLocally = false;//该子view是否需要测量宽度
            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
                // The width of the linear layout will scale, and at least one
                // child said it wanted to match our width. Set a flag
                // indicating that we need to remeasure at least that view when
                // we know our width.
                matchWidth = true;
                matchWidthLocally = true;
            }

            final int margin = lp.leftMargin + lp.rightMargin;
            final int measuredWidth = child.getMeasuredWidth() + margin;
            maxWidth = Math.max(maxWidth, measuredWidth);
            childState = combineMeasuredStates(childState, child.getMeasuredState());//获取子viewmeasure后的state状态

            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
            if (lp.weight > 0) {
                /*
                 * Widths of weighted Views are bogus if we end up
                 * remeasuring, so keep them separate.
                 */
                weightedMaxWidth = Math.max(weightedMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);//这有点看不懂,为何MATCH_PARENT时要用margin
            } else {
                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            }

            i += getChildrenSkipCount(child, i);
        }//for循环结束

        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {//尾部divider高度也加上
            mTotalLength += mDividerHeight;
        }

        if (useLargestChild &&//如果是用最大的子view乘以个数
                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {//高度没限定
            mTotalLength = 0;//计算的都是废物,清零吧
            //重新计算总高度:每个非gone的view的高度都按 上次循环记录的最大子view的高度计算,再加上margin
            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);

                if (child == null) {
                    mTotalLength += measureNullChild(i);
                    continue;
                }

                if (child.getVisibility() == GONE) {
                    i += getChildrenSkipCount(child, i);
                    continue;
                }

                final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                        child.getLayoutParams();
                // Account for negative margins
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
            }
        }

        // 再加上自己的上下内边距
        mTotalLength += mPaddingTop + mPaddingBottom;

        int heightSize = mTotalLength;

        // 再跟自己的minheight对比下
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

        // Reconcile our calculated size with the heightMeasureSpec
        //resolveSizeAndState除了返回最终尺寸信息还会有可能返回量算的state标志位信息。
        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;

        // Either expand children with weight to take up available space or
        // shrink them if they extend beyond our current bounds. If we skipped
        // measurement on any children, we need to measure them now.
        int delta = heightSize - mTotalLength;
        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
            //如果是权重,前面的又计算的都是废物,清零吧
            mTotalLength = 0;

            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);
                if (child.getVisibility() == View.GONE) {
                    continue;
                }

                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

                float childExtra = lp.weight;
                if (childExtra > 0) {
                    // Child said it could absorb extra space -- give him his share
                    int share = (int) (childExtra * delta / weightSum);//给他分享点多出来的高度
                    weightSum -= childExtra;//减掉分享出去的
                    delta -= share;//减掉分享出去的

                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            mPaddingLeft + mPaddingRight +
                                    lp.leftMargin + lp.rightMargin, lp.width);

                    // TODO: Use a field like lp.isMeasured to figure out if this
                    // child has been previously measured
                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
                        // child was measured once already above...
                        // base new measurement on stored values
                        int childHeight = child.getMeasuredHeight() + share;
                        if (childHeight < 0) {
                            childHeight = 0;
                        }
                        //将重新修整好的高度传到子view的measure中
                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
                    } else {
                        // child was skipped in the loop above.
                        // Measure for this first time here
                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
                                        MeasureSpec.EXACTLY));
                    }

                    // Child may now not fit in vertical dimension.
                    childState = combineMeasuredStates(childState, child.getMeasuredState()
                            & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
                }

                final int margin =  lp.leftMargin + lp.rightMargin;
                final int measuredWidth = child.getMeasuredWidth() + margin;
                maxWidth = Math.max(maxWidth, measuredWidth);

                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
                        lp.width == LayoutParams.MATCH_PARENT;

                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);

                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
            }

            // Add in our padding
            mTotalLength += mPaddingTop + mPaddingBottom;
            // TODO: Should we recompute the heightSpec based on the new total length?
        } else {
            alternativeMaxWidth = Math.max(alternativeMaxWidth, weightedMaxWidth);
            // We have no limit, so make all weighted views as tall as the largest child.
            // Children will have already been measured once.
            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
                for (int i = 0; i < count; i++) {
                    final View child = getVirtualChildAt(i);
                    if (child == null || child.getVisibility() == View.GONE) {
                        continue;
                    }

                    final LinearLayout.LayoutParams lp =
                            (LinearLayout.LayoutParams) child.getLayoutParams();

                    float childExtra = lp.weight;
                    if (childExtra > 0) {
                        child.measure(//调用child的measure方法
                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
                                        MeasureSpec.EXACTLY),
                                MeasureSpec.makeMeasureSpec(largestChildHeight,
                                        MeasureSpec.EXACTLY));
                    }
                }
            }
        }

        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
            maxWidth = alternativeMaxWidth;//最大宽度用记录到的最大宽度替代
        }

        maxWidth += mPaddingLeft + mPaddingRight;//加上自己的左右内边距

        // Check against our minimum width
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());//再跟自己的最小宽度对比

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);//最终,设置宽高给自己了

        if (matchWidth) {
            forceUniformWidth(count, heightMeasureSpec);
        }
    }

    private void forceUniformWidth(int count, int heightMeasureSpec) {
        // Pretend that the linear layout has an exact size.
        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
                MeasureSpec.EXACTLY);//自己的宽度
        for (int i = 0; i< count; ++i) {
           final View child = getVirtualChildAt(i);
           if (child.getVisibility() != GONE) {
               LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());

               if (lp.width == LayoutParams.MATCH_PARENT) {
                   // Temporarily force children to reuse their old measured height
                   // FIXME: this may not be right for something like wrapping text?
                   int oldHeight = lp.height;
                   lp.height = child.getMeasuredHeight();

                   // Remeasue with new dimensions
                   measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);//viewgroup的方法
                   lp.height = oldHeight;
               }
           }
        }
    }//measure结束了

    
    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
        ...
    }

   
    int getChildrenSkipCount(View child, int index) {
        return 0;
    }

    int measureNullChild(int childIndex) {
        return 0;
    }

    void measureChildBeforeLayout(View child, int childIndex,
            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
            int totalHeight) {
        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
                heightMeasureSpec, totalHeight);
    }

    int getLocationOffset(View child) {
        return 0;
    }

    int getNextLocationOffset(View child) {
        return 0;
    }

    //也根据自身布局方向分别layout()
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mOrientation == VERTICAL) {
            layoutVertical(l, t, r, b);
        } else {
            layoutHorizontal(l, t, r, b);
        }
    }

    void layoutVertical(int left, int top, int right, int bottom) {
        final int paddingLeft = mPaddingLeft;//左内边距

        int childTop;//每次临时记录的子view的top
        int childLeft;

        // Where right end of child should go
        final int width = right - left;//自己的宽度
        int childRight = width - mPaddingRight;//子view的右边

        // Space available for child
        int childSpace = width - paddingLeft - mPaddingRight;//子view宽度

        final int count = getVirtualChildCount();

        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
        final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;

        switch (majorGravity) {
           case Gravity.BOTTOM:
               // mTotalLength contains the padding already
               childTop = mPaddingTop + bottom - top - mTotalLength;//初始化第一个childTop的值,上内边距+高度-measure后高度
               break;

               // mTotalLength contains the padding already
           case Gravity.CENTER_VERTICAL:
               childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
               break;

           case Gravity.TOP:
           default:
               childTop = mPaddingTop;
               break;
        }

        for (int i = 0; i < count; i++) {
            final View child = getVirtualChildAt(i);
            if (child == null) {
                childTop += measureNullChild(i);//0
            } else if (child.getVisibility() != GONE) {
                final int childWidth = child.getMeasuredWidth();//测量宽高
                final int childHeight = child.getMeasuredHeight();

                final LinearLayout.LayoutParams lp =
                        (LinearLayout.LayoutParams) child.getLayoutParams();

                int gravity = lp.gravity;
                if (gravity < 0) {
                    gravity = minorGravity;
                }
                final int layoutDirection = getLayoutDirection();//方向:RTL相关
                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {//解析横向的位置
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                                + lp.leftMargin - lp.rightMargin;
                        break;

                    case Gravity.RIGHT:
                        childLeft = childRight - childWidth - lp.rightMargin;
                        break;

                    case Gravity.LEFT:
                    default:
                        childLeft = paddingLeft + lp.leftMargin;
                        break;
                }

                if (hasDividerBeforeChildAt(i)) {
                    childTop += mDividerHeight;//这个子view前的divider高度要加进去
                }

                childTop += lp.topMargin;//再加上其上外边距
                setChildFrame(child, childLeft, childTop + getLocationOffset(child),//调用子view的layout()方法
                        childWidth, childHeight);
                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);//再加上自己的下内边距 去下次循环

                i += getChildrenSkipCount(child, i);
            }
        }
    }

    @Override
    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
        super.onRtlPropertiesChanged(layoutDirection);
        if (layoutDirection != mLayoutDirection) {
            mLayoutDirection = layoutDirection;
            if (mOrientation == HORIZONTAL) {
                requestLayout();
            }
        }
    }

   
    void layoutHorizontal(int left, int top, int right, int bottom) {
        ...
    }

    private void setChildFrame(View child, int left, int top, int width, int height) {
        child.layout(left, top, left + width, top + height);
    }

    //(MarginLayoutParams extends ViewGroup.LayoutParams)
    //ViewGroup.LayoutParams中是height和weight
    //MarginLayoutParams中是上下左右的一个margin值
    //此处的LayoutParams拓展了weight 和gravity属性
    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
        
        public float weight;
        public int gravity = -1;

        }
        ...
    }
}
复制代码

zyt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值