详解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。

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

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

相关文章

  • 魔兽世界9.0兽王猎天赋盟约选择及输出手法教学 兽王入门指南

    魔兽世界9.0兽王猎天赋盟约选择及输出手法教学 一、天赋选择 作为兽王猎的玩家,我们在选取天赋时应该注重以下几点: 1、第一行天赋 第一行天赋的选择主要分为两种,分别是屠宰和狂野呼唤。如果我们更注重单体伤害的话,那么就选择屠宰;如果我们更注重团队的贡献,加上副本中有各种各样的光环,那么就需要选择狂野呼唤。 2、第二行天赋 第二行天赋的选择主要分为两种,分别是…

    other 2023年6月27日
    00
  • c++中的两种getline用法详解

    C++中的两种getline用法详解 在C++中,getline函数是一个十分常用的函数,它的作用是从输入流中读取一行数据并存放到一个string类型的变量中。但实际上,C++中有两种不同的getline函数使用方式,这里将对它们进行详细讲解。 getline(istream& is, string& str) 这种用法是getline函数的…

    other 2023年6月26日
    00
  • Spring Boot搭建文件上传服务的方法

    下面是详细讲解“Spring Boot搭建文件上传服务的方法”的完整攻略。 1. 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId&gt…

    other 2023年6月27日
    00
  • 详解微信小程序登录获取unionid

    详解微信小程序登录获取unionid 微信小程序登录获取unionid是一个常见的需求,本文将提供一个完整的攻略,介绍微信小程序登录获取unionid的过程和方法,并提两个示例说明。 准备工作 在进行微信小程序登录获取unionid之前,需要进行以下准备工作: 在微公众平台上创建小程序,并获取小程序的AppID和AppSecret。 在小程序中使用wx.lo…

    other 2023年5月8日
    00
  • SpringBoot 如何添加容器启动的初始化逻辑的操作方法

    下面是关于SpringBoot添加容器启动的初始化逻辑的完整攻略。 1. 概述 在SpringBoot中,我们可以通过添加容器启动的初始化逻辑来对应用进行一些自定义操作,例如初始化数据源连接池、加载定时任务等。 在整个启动过程中,SpringBoot会在特定的时刻调用我们设置的初始化逻辑接口。 2. 添加初始化逻辑 2.1 通过实现接口方式 对于简单的场景,…

    other 2023年6月20日
    00
  • 3.live555源码分析—延时队列

    3.live555源码分析—延时队列 在live555的源码中,有一个名为”DelayedTaskQueue”的类,被用作事件调度系统中的延时事件队列。 它由系统上的多个任务和回调组成,负责在需要时自动调用这些任务和回调。 在本文中,我们将深入研究live555的源码实现,以便更好地理解延时队列的原理和功能。 1. DelayedTaskQueue类 D…

    其他 2023年3月28日
    00
  • 以太坊9月推出新测试网Holeky!解决Goerli测试币问题

    以太坊9月推出新测试网Holeky!解决Goerli测试币问题攻略 以太坊将于9月推出新的测试网Holeky,旨在解决Goerli测试币问题。本攻略将详细介绍如何使用Holeky测试网进行开发和测试。 步骤一:安装以太坊客户端 首先,您需要安装以太坊客户端,以便连接到Holeky测试网。以下是安装以太坊客户端的示例命令: $ npm install -g g…

    other 2023年7月27日
    00
  • mybatis-plus之自动映射字段(typeHandler)的注意点及说明

    下面是详细的攻略,包括自动映射字段和typeHandler的注意点及示例说明。 1. 什么是mybatis-plus的自动映射字段 Mybatis-Plus中的自动映射字段指的是ORM框架通过对象和表结构的映射关系,在数据操作时自动完成对象属性和表字段之间的映射。即当我们使用Mybatis-Plus进行数据库操作时,我们不需要手动编写SQL语句,只需要编写J…

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