乐趣区

关于android:Android中View的工作流程之layout过程

PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接

ps:文章是基于 Android Api 31 来剖析源码的。

目录

1、View 的 layout 过程

 1、1 View(它不是 ViewGroup)的 layout 过程

      1、1、1 原始 View 的 layout 过程

      1、1、2 具体 View 的 layout 过程

 1、2 ViewGroup 的 layout 过程


1、View 的 layout 过程

layout 的过程是 ViewGroup 用来确定子元素的地位,当 ViewGroup 的地位被确定后,如果这个 View 是 ViewGroup 并且有子元素的话,它在 onLayout 中 会遍历所有的子元素并调用其 layout 办法,在 layout 办法中 onLayout 办法又会被调用;上面咱们先说一下非 ViewGroup 的 layout 过程。

1、1 View(它不是 ViewGroup)的 layout 过程

1、1、2 原始 View 的 layout 过程

咱们先看一下 View 的 layout 办法;

图片

先看正文 2,onLayout 办法是确定以后 View 的所有子元素的地位,咱们看看 View 的 onLayout 办法;

图片

看到没,View 的 onLayout 办法是一个空实现,所以间接用 View 这个类作为 xml 文件中的标签的话是没有子元素的。

好,咱们当初看一下正文 1 中的 setFrame 办法,这个办法位于 View 这个类中;

图片

看正文 3 这 4 个变量的赋值,left、top、right 和 bottom 就是以后这个 View 的 4 个顶点的地位,所以说 setFrame 办法就是确定以后 View 的在父容器布局中的地位。

1、1、2 具体 View 的 layout 过程

我这里介绍的具体的 View,它不是 ViewGroup 哦,它是继承于 View 的子类或者是 View 的子类的子类等等,例如 TextView、ImageView、Button 等;好,咱们这里就用 TextView 的 layout 过程来剖析,TextView 并没有重写 layout 办法,只是重写了 onLayout 办法;

图片

看 TextView 中的 onLayout 办法,它调用了 super.onLayout 办法,而 super 就是 View,所以 super.onLayout 是空实现;看正文 4 的办法,它只是主动计算和设置文本大小,所以 TextView 的地位设置是在 View 的 setFrame 办法中,同时证实 TextView 它是不可能作为其余 View 的父容器。

1、2 ViewGroup 的 layout 过程

咱们晓得,Android 中零碎自带的 ViewGroup 实现类有 RelativeLayout、LinearLayout、GridLayout、TableLayout、FrameLayout 和 Constraint-Layout,因为 ViewGroup 子类的布局个性不同,所以 ViewGroup 子类对子元素的地位设置也就不同了;这里咱们用 FrameLayout 的 layout 过程进行剖析,同样 FrameLayout 没有重写 layout 办法,它的父类 ViewGroup 也没有重写 layout 办法,咱们只需看它的 onLayout 办法就好了;

图片

FrameLayout 的 onLayout 办法调用了 FrameLayout 的 layoutChildren 办法,咱们往下看 layoutChildren 办法;

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {

    //5、final int count = getChildCount();

    //6、final int parentLeft = getPaddingLeftWithForeground();
    final int parentRight = right - left - getPaddingRightWithForeground();

    final int parentTop = getPaddingTopWithForeground();
    final int parentBottom = bottom - top - getPaddingBottomWithForeground();

    for (int i = 0; i < count; i++) {final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            ......
            //7、final int layoutDirection = getLayoutDirection();
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
            final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

            //8、switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.CENTER_HORIZONTAL:
                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                            lp.leftMargin - lp.rightMargin;
                    break;
                case Gravity.RIGHT:
                    if (!forceLeftGravity) {
                        childLeft = parentRight - width - lp.rightMargin;
                        break;
                    }
                case Gravity.LEFT:
                default:
                    childLeft = parentLeft + lp.leftMargin;
            }

            //9、switch (verticalGravity) {
                case Gravity.TOP:
                    childTop = parentTop + lp.topMargin;
                    break;
                case Gravity.CENTER_VERTICAL:
                    childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                            lp.topMargin - lp.bottomMargin;
                    break;
                case Gravity.BOTTOM:
                    childTop = parentBottom - height - lp.bottomMargin;
                    break;
                default:
                    childTop = parentTop + lp.topMargin;
            }

            //10、child.layout(childLeft, childTop, childLeft + width, childTop + height);
        }
    }

}

看正文 5,它是获取子元素的个数;看正文 6 中的 parentLeft、parentRight、parentTop 和 parentBottom,是获取以后的 FrameLayout 左右高低这四个方向的 padding;看正文 7,依据 View 中 LayoutParams 的 Gravity 来计算 FrameLayout 子 View 的四个顶点地位,看是左到右还是右到左;看正文 8,依据横坐标来计算子 View 的 left 地位;看正文 9,依据纵坐标来计算子 View 的 top 地位;看正文 10,晓得了子 View 的 left、top、width 和 height 后,递归调用子 View 的 layout 过程并顺便对子 View 的地位进行保留。

在 Android 中 View 的工作流程之 measure 过程这篇文章咱们有提到过,个别状况下测量的宽低等于最终的宽高对不对?只不过测量宽高造成于 View 的 measure 过程,而最终宽高造成于 View 的 layout 过程,只是这两者的赋值机会不同而已,最终宽高的赋值机会略微晚一些,所以,在平时开发中,咱们能够认为 View 的测量宽低等于最终宽高;然而一些非凡状况下测量宽高和最终宽高不一样,上面我举个例子;

图片

正文 11 的代码会导致在任何状况下 View 的最终宽高比测量宽高大 40 px,

这样会导致 View 显示不失常,这实了测量宽高真的能够不等于最终宽高,但理论开发中,咱们不举荐这样做;有时候,View 须要屡次 measure 过程能力确定本人的测量宽高,在前几次的测量过程中,其得出的测量宽高很有可能和最终宽高不一样,到最初,测量宽高和最终宽高是一样的。

退出移动版