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

相关文章

  • qq离线文件保存在哪里

    QQ离线文件是指在QQ聊天过程中,对方发送给我们的文件,我们选择保存到本地,在离线状态下可以查看的文件。这些文件存储在我们的电脑硬盘中,不同操作系统的存储路径不同。 下面是QQ离线文件在不同操作系统下的存储路径: Windows操作系统 在Windows操作系统下,QQ的离线文件默认存储在用户目录下的“\My Documents\Tencent Files\…

    其他 2023年4月16日
    00
  • ANDROID应用程序的混淆打包分享

    下面我将详细讲解“Android应用程序的混淆打包分享”的完整攻略。 什么是混淆打包? 混淆打包(Proguard)是Android开发中常用的一种技术,用于将源代码中的变量名、方法名等符号混淆处理,旨在增加程序的安全性和难度,防止恶意开发者通过反编译等手段获取源代码。 混淆打包的作用: 混淆打包可达到以下作用: 增强代码安全性,防止恶意反编译; 缩小程序包…

    other 2023年6月25日
    00
  • mysql 查看当前使用的配置文件my.cnf的方法(推荐)

    要查看当前MySQL使用的配置文件my.cnf的方法,可以按照以下步骤进行操作: 进入MySQL命令行: mysql -u root -p 该命令中,-u选项用于指定MySQL用户名,-p选项用于提示输入MySQL用户密码。 查看当前MySQL的变量值: SHOW VARIABLES; 该命令用于查看当前MySQL服务器的变量及其对应的值。其中,可以查看到m…

    other 2023年6月25日
    00
  • 足球经理2016游戏跳出的解决方法

    针对足球经理2016游戏跳出的问题,完整的解决方法如下: 问题描述 在玩足球经理2016游戏时,可能会出现游戏直接跳出的情况,玩家无法继续进行游戏,这是一个常见的问题。 解决方法 方法一:检查游戏配置要求是否符合 首先检查自己的电脑是否满足游戏的基本配置要求,如果配置不足,可能会导致游戏跳出的情况。 在官方网站上查看游戏的具体配置要求,比如CPU、内存、显卡…

    other 2023年6月27日
    00
  • 【mq读书笔记】消息拉取长轮训机制(Broker端)

    【mq读书笔记】消息拉取长轮训机制(Broker端) 在消息中间件的分发系统中,长轮询是一种优化消息队列性能的方式。具体地说,它允许消费者在消息队列上等待新的消息,直到队列中有新的消息才返回结果,从而减少消息队列的轮询次数,提高消息的传输效率。下面我们将介绍消息拉取长轮训机制在Broker端的实现方式。 首先,Broker端需要提供一个RESTful API…

    其他 2023年3月28日
    00
  • ECC 构筑安全可靠的区块链

    ECC 构筑安全可靠的区块链 区块链技术的应用正在越来越广泛地渗透到我们生活的方方面面。然而,随着区块链技术的深入发展,一些以前不曾被人关注的问题也逐渐浮出水面,比如区块链的安全性问题。 在区块链中,加密算法是保证隐私和安全的重要手段之一。而可植入的加密算法竞赛(ECC)则是一个目前广泛应用在区块链中的加密算法。下面将介绍ECC在构筑安全可靠的区块链中扮演的…

    其他 2023年3月28日
    00
  • Element Popover 弹出框的使用示例

    Element Popover 弹出框的使用示例攻略 Element Popover 是一个常用的界面组件,用于在用户点击或悬停在某个元素上时显示相关的信息或操作选项。下面是 Element Popover 的使用示例攻略,包含两个具体的示例说明。 示例一:鼠标悬停显示信息 在这个示例中,我们将展示如何使用 Element Popover 在鼠标悬停时显示相…

    other 2023年7月28日
    00
  • visualstudio怎么调整输出继承对象的大小?

    调整Visual Studio中输出继承对象大小的方法有两种。下面将对这两种方法进行详细的讲解。 方法一:使用调试窗口查看继承对象 在代码中打上断点,使程序停在需要查看的继承对象的位置。 在 Visual Studio 工具栏中选择 “调试” -> “窗口” -> “快速监视” 或使用快捷键 “Shift+Ctrl+Q” 打开窗口。 在快速监视窗…

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