详解Android布局加载流程源码

详解Android布局加载流程源码的攻略如下:

1. 确定需求

在详细讲解Android布局加载流程源码之前,需要先了解本次攻略的目的和需求。本次攻略的目的是通过对Android布局加载流程源码的分析,深入了解Android布局加载的过程和原理,以便进一步优化相关的开发工作。

2. 学习布局加载流程

2.1 布局加载的基本流程

Android布局加载的基本流程分为3个阶段,即解析XML文件、创建View树和测量、布局和绘制View。其中,解析XML文件的过程是由LayoutInflator类实现的,创建View树和测量、布局和绘制View的过程则是由ViewGroup类和View类实现的。

2.2 解析XML文件的源码分析

LayoutInflator类的主要作用是将XML文件解析成View对象,其源码分析过程如下:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    final LayoutInflaterDelegate delegate = this.mDelegate;
    if (delegate != null) {
        return delegate.onCreateView(null, null, parser);
    }
    synchronized (mConstructorArgs) {
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = mContext;
        View result = root;
        try {
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                   type != XmlPullParser.END_DOCUMENT) {
                // Empty loop.
            }
            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }
            final String name = parser.getName();
            if (TAG_MERGE.equals(name)) {
                if (root == null || !attachToRoot) {
                    throw new InflateException("merge can be used only with a valid "
                            + "ViewGroup root and attachToRoot=true");
                }
                rInflate(parser, root, mContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml
                View temp = rInflate(parser, root, mContext, attrs, false);
                if (root != null && attachToRoot) {
                    root.addView(temp);
                }
                result = temp;
            }
        } finally {
            mConstructorArgs[0] = lastContext;
        }
        return result;
    }
}

在该源码中,首先获取了解析器的属性集,然后通过一个while循环遍历XML文件,直到找到根元素(即XML文件的第一个元素)。接着,通过调用rInflate()方法创建View树。在创建View树过程中,如果该XML文件根元素设有merge标签,则需要结合父View来创建View树,否则直接为根元素创建View树。最后,如果attachToRoot参数为true,则将创建的View加入到根ViewGroup中。

2.3 创建View树和测量、布局和绘制View的源码分析

创建View树和测量、布局和绘制View的过程由ViewGroup类和View类共同完成,其源码分析过程如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //遍历子View,对View进行测量
    measureChildren(widthMeasureSpec, heightMeasureSpec);

    //设置自身宽高
    int width = 0;
    int height = 0;
    int count = getChildCount();
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            width += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            height += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
        }
    }
    setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
            resolveSizeAndState(height, heightMeasureSpec, 0));
}

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    int childLeft = 0;
    int childTop = 0;

    int count = getChildCount();
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            final int childWidth = child.getMeasuredWidth();
            final int childHeight = child.getMeasuredHeight();
            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            child.layout(childLeft + lp.leftMargin, childTop + lp.topMargin,
                    childLeft + childWidth + lp.rightMargin,
                    childTop + childHeight + lp.bottomMargin);

            childLeft += childWidth + lp.leftMargin + lp.rightMargin;
            childTop += childHeight + lp.topMargin + lp.bottomMargin;
        }
    }
}

protected void onDraw(Canvas canvas) {
    // 遍历子View,对View进行绘制
    super.onDraw(canvas);
    int count = getChildCount();
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            drawChild(canvas, child, getDrawingTime());
        }
    }
}

在该源码中,onMeasure()方法主要实现了对View的测量,onLayout()方法主要实现了对View的布局,onDraw()方法主要实现了对View的绘制。

3. 举例说明

3.1 自定义ViewGroup的示例

以下是一个自定义LinearLayout的示例,可以通过继承ViewGroup类实现:

public class CustomLinearLayout extends ViewGroup {

    public CustomLinearLayout(Context context) {
        super(context);
    }

    public CustomLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childLeft = 0;
        int childTop = 0;

        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final int childWidth = child.getMeasuredWidth();
                final int childHeight = child.getMeasuredHeight();
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                child.layout(childLeft + lp.leftMargin, childTop + lp.topMargin,
                        childLeft + childWidth + lp.rightMargin,
                        childTop + childHeight + lp.bottomMargin);

                childLeft += childWidth + lp.leftMargin + lp.rightMargin;
                childTop += childHeight + lp.topMargin + lp.bottomMargin;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int maxChildHeight = 0;
        int totalWidth = 0;

        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                totalWidth += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            }
        }

        setMeasuredDimension(
                View.resolveSize(totalWidth, widthMeasureSpec),
                View.resolveSize(maxChildHeight, heightMeasureSpec)
        );
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }
}

该自定义LinearLayout继承了ViewGroup类,并重写了onLayout()方法和onMeasure()方法。onMeasure()方法的作用是先通过measureChildren()方法测量所有子View的大小,然后确定自身的大小。而onLayout()方法,则是实现对所有子View进行布局,这里实现的方式是按照水平方向排列,每个子View之间保留一定的Margin。

3.2 布局文件的示例

以下是一个简单的布局文件的示例,可以对TextView进行布局:

<com.example.CustomLinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Username:" />

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxLines="1"
        android:ellipsize="end"
        android:inputType="textEmailAddress"
        android:layout_marginLeft="10dp" />

</com.example.CustomLinearLayout>

这个布局文件使用了自定义的CustomLinearLayout作为根ViewGroup,该CustomLinearLayout的方向是水平的,因此它的子View(一个TextView和一个EditText)会按照水平方向进行排列。其中,TextView距下一个View有10dp的Margin。

阅读剩余 80%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Android布局加载流程源码 - Python技术站

(0)
上一篇 2023年6月25日
下一篇 2023年6月25日

相关文章

  • ibmappscan基本操作手册

    IBM AppScan 基本操作手册 IBM AppScan 是一款用于 Web 应用程序安全测试的工具,它可以帮助用户发现 Web 应用程序的安全漏洞和风险。在本攻略中,我们将介如何使用 IBMScan 进行基本操作,并提供两个示例说明。 安装 在使用 IBM AppScan 之前,您需要先安装它。以下是装 IBM AppScan 的步骤: 下载 IBM …

    other 2023年5月6日
    00
  • Redis事务处理的使用操作方法

    以下是关于Redis事务处理的使用操作方法的完整攻略: 开启事务:使用MULTI命令来开启一个事务。事务中的所有命令都将被放入一个队列中,直到事务被执行。 示例说明1:开启事务 MULTI 2. **执行事务**:使用`EXEC`命令来执行事务中的所有命令。Redis会按照命令在队列中的顺序依次执行。 示例说明2:执行事务 “`markdown EXEC …

    other 2023年10月18日
    00
  • Python 无限级分类树状结构生成算法的实现

    Python 无限级分类树状结构生成算法的实现 算法介绍 Python 无限级分类树状结构生成算法用于将任意多层级别的数据转化为树状结构,方便数据的展示和处理。该算法通过递归的方式实现,可以适用于各种类型的分类数据,如商品分类、学科分类等。 算法实现步骤 准备原始数据 数据格式需要满足以下要求: 每一条数据至少包含一个唯一标识符和一个分类名称; 如果数据有层…

    other 2023年6月27日
    00
  • win11安装软件报错怎么办 win11安装软件提示错误的解决方法

    win11安装软件报错怎么办 1. 查看错误信息 在进行软件安装时,出现错误提示时,首先需要查看错误信息。根据提示信息,确定出现错误的原因,才能进行下一步的解决。 2. 确认软件是否兼容Win11 Win11是最新的操作系统,一些软件可能还没有适配该版本的系统。因此,需要确认软件是否兼容Win11。可以在软件官网或者相关论坛寻找解决方法。 3. 以管理员身份…

    other 2023年6月28日
    00
  • 微信开发者工具怎么导入小程序项目?微信开发者工具导入项目教程

    首先,需要确保你已经完成了小程序项目的开发和打包。接下来,我们讲解微信开发者工具怎么导入小程序项目。 步骤一:打开微信开发者工具 首先,在电脑上打开已经安装好的微信开发者工具。如果你还没有安装微信开发者工具,可以前往微信官方开发者文档下载并安装。 步骤二:导入小程序项目 在微信开发者工具主界面,点击左上角的“新建项目”按钮。然后,在弹出的页面中,选择“导入项…

    other 2023年6月26日
    00
  • Counter-Strike: cstrike.exe 应用程序错解决方法误

    解决 “Counter-Strike: cstrike.exe 应用程序错解决方法误” 问题,可以遵循以下步骤: 步骤一:重新安装游戏 首先,建议你尝试重新安装 Counter-Strike 游戏,因为该错误有可能是由游戏程序损坏或文件丢失造成的。 可以先卸载 Counter-Strike,然后再重新下载安装,确保下载的游戏文件没有损坏并且安装过程没有中途出…

    other 2023年6月25日
    00
  • Swift教程之继承详解

    Swift教程之继承详解 简介 在面向对象编程语言中,继承是一个重要的概念。它可以让一个类拥有另一个类的属性和方法,并且还可以扩展自己的功能。在Swift中,继承是通过class关键字来实现的。 继承的语法 一个类可以继承另一个类,通过在类名后面加上一个冒号”:”以及被继承的类的名称,例如: class SubClass: SuperClass { // 代…

    other 2023年6月26日
    00
  • Win10虚拟内存怎么设置?Win10设置虚拟内存的方法

    Win10虚拟内存设置攻略 什么是虚拟内存? 虚拟内存是计算机系统中的一种技术,它允许操作系统将部分硬盘空间用作内存扩展,以便处理大量的数据和程序。在Windows 10中,你可以手动设置虚拟内存的大小和位置。 设置虚拟内存的步骤 以下是在Windows 10中设置虚拟内存的步骤: 打开“控制面板”:点击开始菜单,然后在搜索栏中输入“控制面板”,并选择打开它…

    other 2023年8月1日
    00
合作推广
合作推广
分享本页
返回顶部