LinearLayout实现原理分析

LinearLayout 是 Android 中最常用的布局之一,它负责按照水平或垂直方向排列其子视图。LinearLayout 的实现原理主要集中在测量和布局两个阶段,这两个阶段分别对应于 onMeasure()onLayout() 方法。

LinearLayout 的测量过程 (onMeasure())

LinearLayoutonMeasure() 方法中,主要任务是测量所有子视图的大小,并根据父布局传递的测量规格来确定自身的大小。测量过程大致分为以下几个步骤:

  1. 测量模式和大小计算

    • 获取从父布局传递下来的测量规格和尺寸。
    • 计算可用的空间尺寸,考虑布局参数中的权重(weight)。
  2. 子视图测量

    • 遍历所有子视图,根据当前的测量模式和尺寸,调用每个子视图的 measure() 方法进行测量。
    • 如果设置了权重(weight),则需要根据可用空间和权重比重新分配尺寸。
  3. 确定自身大小

    • 根据子视图的测量结果和布局方向,确定 LinearLayout 自身的大小。
    • 考虑到 padding 和 margin 的影响。
  4. 设置测量尺寸

    • 最终调用 setMeasuredDimension() 方法来设置 LinearLayout 的测量尺寸。

LinearLayout 的布局过程 (onLayout())

LinearLayoutonLayout() 方法中,主要任务是根据测量结果,将子视图放置在正确的位置。布局过程如下:

  1. 初始化变量

    • 初始化一些变量,如当前的 x 和 y 坐标,用于跟踪子视图的放置位置。
  2. 遍历子视图

    • 遍历所有子视图,根据子视图的测量尺寸和布局方向(水平或垂直),计算并设置子视图的最终位置。
    • 考虑到子视图的 margin,以确保正确的间距。
  3. 放置子视图

    • 调用 layout() 方法,传入子视图的左、顶、右、底坐标,完成子视图的放置。

源码分析

以下是 LinearLayoutonMeasure()onLayout() 方法的基本流程,基于源码的简化描述:

onMeasure()
1@Override
2protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
3    // 计算可用宽度和高度
4    final int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
5    final int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
6    
7    // 考虑到 padding
8    final int widthPadding = getPaddingLeftWithForeground() + getPaddingRightWithForeground();
9    final int heightPadding = getPaddingTopWithForeground() + getPaddingBottomWithForeground();
10    
11    // 测量子视图
12    measureChildren(widthMeasureSpec, heightMeasureSpec);
13    
14    // 根据子视图的测量结果确定 LinearLayout 的大小
15    setMeasuredDimension(
16        resolveSizeAndState(getDesiredWidth(), widthMeasureSpec, 0),
17        resolveSizeAndState(getDesiredHeight(), heightMeasureSpec, 0)
18    );
19}
onLayout()
1@Override
2protected void onLayout(boolean changed, int l, int t, int r, int b) {
3    final int count = getChildCount();
4    final int parentLeft = getPaddingLeftWithForeground();
5    final int parentRight = r - l - getPaddingRightWithForeground();
6    final int parentTop = getPaddingTopWithForeground();
7    final int parentBottom = b - t - getPaddingBottomWithForeground();
8    final int width = parentRight - parentLeft;
9    final int height = parentBottom - parentTop;
10    
11    int childLeft = parentLeft;
12    int childTop = parentTop;
13    
14    for (int i = 0; i < count; i++) {
15        final View child = getChildAt(i);
16        
17        if (child.getVisibility() != GONE) {
18            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
19            
20            final int childWidth = child.getMeasuredWidth();
21            final int childHeight = child.getMeasuredHeight();
22            
23            child.layout(childLeft + lp.leftMargin, childTop + lp.topMargin,
24                         childLeft + lp.leftMargin + childWidth,
25                         childTop + lp.topMargin + childHeight);
26            
27            if (isHorizontal()) {
28                childLeft += childWidth + lp.rightMargin;
29            } else {
30                childTop += childHeight + lp.bottomMargin;
31            }
32        }
33    }
34}

请注意,这里的代码是基于对源码的理解进行了简化和重写,以帮助理解 LinearLayout 的核心逻辑。实际的源码会更复杂,包括对权重的处理、性能优化以及对多种特殊情况的处理。

LinearLayout 的测量和布局

1. 测量子视图 (measureChildren)

LinearLayoutonMeasure() 方法中,measureChildren() 方法用于测量所有子视图。这个方法会调用每个子视图的 measure() 方法,根据父布局提供的测量规格来确定子视图的尺寸。

1void measureChildren(int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
2    final int count = getChildCount();
3    for (int i = 0; i < count; i++) {
4        final View child = getChildAt(i);
5        if (child.getVisibility() != GONE) {
6            LayoutParams lp = (LayoutParams) child.getLayoutParams();
7            child.measure(getChildMeasureSpec(parentWidthMeasureSpec,
8                    mTotalLength + getPaddingLeft() + getPaddingRight(), lp.width),
9                    getChildMeasureSpec(parentHeightMeasureSpec,
10                            getPaddingTop() + getPaddingBottom(), lp.height));
11        }
12    }
13}

这里的关键在于 getChildMeasureSpec() 方法,它会根据子视图的宽度和高度规格、父布局的测量规格、以及子视图的 layout params 来生成子视图的测量规格。

2. 布局子视图 (onLayout)

LinearLayoutonLayout() 方法中,子视图的位置是根据布局方向、子视图的大小、以及子视图与父布局之间的 margin 来确定的。

1@Override
2protected void onLayout(boolean changed, int l, int t, int r, int b) {
3    final int count = getChildCount();
4    final int parentLeft = getPaddingLeft();
5    final int parentRight = r - l - getPaddingRight();
6    final int parentTop = getPaddingTop();
7    final int parentBottom = b - t - getPaddingBottom();
8    final int parentWidth = parentRight - parentLeft;
9    final int parentHeight = parentBottom - parentTop;
10
11    int widthUsed = 0;
12    int heightUsed = 0;
13    final boolean measureHeight = isModeExactly(mMeasureSpecModeWidth);
14    final boolean measureWidth = isModeExactly(mMeasureSpecModeHeight);
15
16    // 遍历所有子视图,布局它们
17    for (int i = 0; i < count; i++) {
18        final View child = getChildAt(i);
19        if (child.getVisibility() != GONE) {
20            LayoutParams lp = (LayoutParams) child.getLayoutParams();
21            final int childLeft;
22            final int childTop;
23            final int childWidth;
24            final int childHeight;
25
26            if (mOrientation == VERTICAL) {
27                childLeft = parentLeft + lp.leftMargin;
28                childTop = parentTop + heightUsed + lp.topMargin;
29                childWidth = parentWidth - lp.leftMargin - lp.rightMargin;
30                childHeight = child.getMeasuredHeight();
31                heightUsed += childHeight + lp.topMargin + lp.bottomMargin;
32            } else {
33                childLeft = parentLeft + widthUsed + lp.leftMargin;
34                childTop = parentTop + lp.topMargin;
35                childWidth = child.getMeasuredWidth();
36                childHeight = parentHeight - lp.topMargin - lp.bottomMargin;
37                widthUsed += childWidth + lp.leftMargin + lp.rightMargin;
38            }
39
40            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
41        }
42    }
43}

这里的关键在于确定子视图的左上角坐标 (childLeft, childTop) 和子视图的宽高 (childWidth, childHeight)。这些值取决于布局的方向(水平或垂直)和子视图的布局参数。

3. 响应权重 (weight)

当子视图具有权重属性时,LinearLayout 会根据可用的空间和权重的比例来分配额外的空间给子视图。权重响应发生在测量子视图的阶段。

1final int widthSpec;
2final int heightSpec;
3
4if (mWeightSum > 0 && measureWidth) {
5    final int widthWithoutWeights = mTotalLength + getPaddingLeft() + getPaddingRight();
6    final int widthAvailable = parentWidth - widthWithoutWeights;
7    widthSpec = MeasureSpec.makeMeasureSpec((int) ((float) widthAvailable * weight), MeasureSpec.EXACTLY);
8} else {
9    widthSpec = getChildMeasureSpec(parentWidthMeasureSpec,
10            mTotalLength + getPaddingLeft() + getPaddingRight(), lp.width);
11}
12
13if (mWeightSum > 0 && measureHeight) {
14    final int heightWithoutWeights = mTotalLength + getPaddingTop() + getPaddingBottom();
15    final int heightAvailable = parentHeight - heightWithoutWeights;
16    heightSpec = MeasureSpec.makeMeasureSpec((int) ((float) heightAvailable * weight), MeasureSpec.EXACTLY);
17} else {
18    heightSpec = getChildMeasureSpec(parentHeightMeasureSpec,
19            getPaddingTop() + getPaddingBottom(), lp.height);
20}
21
22child.measure(widthSpec, heightSpec);

在上面的代码片段中,mWeightSum 表示所有子视图权重的总和,widthAvailableheightAvailable 分别表示可用于分配给有权重的子视图的剩余宽度和高度。如果权重总和大于零,并且测量模式是确切的,那么会根据权重比例分配空间给子视图。

总结

LinearLayout 的实现原理主要集中在如何测量和布局其子视图,以满足指定的布局方向和权重响应。通过理解 onMeasure()onLayout() 方法,以及子视图测量规格的生成,可以更好地掌握 LinearLayout 的内部工作原理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47459.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Template_C++

C模板 C提供了function template. function template&#xff1a;实际上是建立一个通用函数&#xff0c;其函数类型和形参类型不具体制定&#xff0c;用一个虚拟的类型来代表。这个通用的函数就称为函数模版。 是不是可以这样理解&#xff0c;函数模版就是给了一种功能&…

CVC语言参考

声明 比特向量表达式&#xff08;或术语&#xff09;是由比特向量常量、比特向量变量以及下列函数构成的。在 STP 中&#xff0c;所有变量必须在使用之前声明。一个长度为 32 的比特向量变量的声明示例如下&#xff1a; x : BITVECTOR(32);一个数组声明的示例如下&#xff1a…

pytorch学习(四)绘制loss和correct曲线

这一次学习的时候静态绘制loss和correct曲线&#xff0c;也就是在模型训练完成后&#xff0c;对统计的数据进行绘制。 以minist数据训练为例子 import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets from torchvisi…

python+pygame实现扫雷游戏之二

接pythonpygame实现扫雷游戏之一&#xff0c;继续写游戏局的类&#xff1a; 五、 mineblock.py # -*- coding: utf-8 -*- import randomfrom blockstatus import * from mine import *# 9*9-10 16*16-40 30*16-99 30*24-** # BLOCK_WIDTH 30 # BLOCK_HEIGHT 16 # MIN…

【zabbix6监控java-tomcat全流程】

目录 一、监控主机安装zabbix-server1、zabbix的安装2、配置数据库3、为zabbix server配置数据库4、启动服务,web界面安装 二、被监控主机安装tomcat1、安装JDK2、安装tomcat 三、zabbix的服务端安装zabbix-java-gateway四、被监控主机tomcat的配置五、web界面添加主机 一、监控…

使用 Web APi - MediaRecorder 获取麦克风资源,报错:Cannot find name ‘MediaRecorder‘ 的解决方法

目录 一、背景&#xff1a; 二、具体解决方法 一、背景&#xff1a; angular 调用 MediaRecorder 来使用麦克风获取声音&#xff0c;&#xff08;具体要求&#xff1a;angular 前端 按键调用 麦克风&#xff0c;松开按键生成音频文件&#xff09;代码如下&#xff08;来自通…

【树莓派3B+】控制引脚输出高低电平

前言一、安装RPI.GPIO库二、编写简单的输出高低电平的程序三、运行程序总结 前言 首先检查一下自己的板子有没有带库 我这个是有的。 ok&#xff0c;正式进入步骤 一、安装RPI.GPIO库 如果还没有安装RPi.GPIO库&#xff0c;可以通过以下命令在树莓派上安装&#xff1a; p…

Ubuntu20.04从零开搭PX4MavrosGazebo环境并测试

仅仅是个人搭建记录 参考链接&#xff1a; https://zhuanlan.zhihu.com/p/686439920 仿真平台基础配置&#xff08;对应PX4 1.13版&#xff09; 语雀 mkdir -p ~/tzb/catkin_ws/src mkdir -p ~/tzb/catkin_ws/scripts cd catkin_ws && catkin init catkin build cd…

数据结构day2

一、思维导图 内存分配 二、课后习题 分文件编译 //sys.h #ifndef TEST_H #define TEST_H #define MAX_SIZE 100//定义学生类型 typedef struct Stu {char name[20]; //姓名int age; //年龄double score; //分数 }stu;//定义班级类型 typedef struct Class {struct …

Postman实战案例:从零开始设计API测试流程

Postman实战案例&#xff1a;从零开始设计API测试流程 API测试在现代软件开发中至关重要。Postman作为一款强大的API测试工具&#xff0c;不仅提供了直观的用户界面&#xff0c;还支持自动化测试、环境配置和脚本编写。本文将从零开始&#xff0c;详细介绍如何使用Postman设计…

php在服务器上部署可视化运维工具详细列表

如果你的项目很小&#xff0c;可以用宝塔。但对于并发大的服务器管理&#xff0c;选择适合的工具非常重要。 目录 以下是一些免费且适合高并发管理的服务器管理工具推荐&#xff1a; 1Panel&#xff1a; Portainer&#xff1a; Webmin&#xff1a; Cockpit&#xff1a; …

实战:详解Spring创建bean的流程(图解+示例+源码)

概叙 这篇主要总结Spring中bean的创建过程&#xff0c;主要分为加载bean信息–>实例化bean–>属性填充–>初始化阶段–>后置处理等步骤&#xff0c;且每个步骤Spring做的事情都很多&#xff0c;这块源码还是很值得我们都去看一看的。而Spring中Bean的声明周期其实…

GEO数据挖掘从数据下载处理质控到差异分析全流程分析步骤指南

0. 综合的教学视频介绍 GEO数据库挖掘分析作图全流程每晚11点在线教学直播录屏回放视频&#xff1a; https://www.bilibili.com/video/BV1rm42157CT/ GEO数据从下载到各种挖掘分析全流程详解&#xff1a; https://www.bilibili.com/video/BV1nm42157ii/ 一篇今年近期发表的转…

捷配总结的SMT工厂安全防静电规则

SMT工厂须熟记的安全防静电规则&#xff01; 安全对于我们非常重要&#xff0c;特别是我们这种SMT加工厂&#xff0c;通常我们所讲的安全是指人身安全。 但这里我们须树立一个较为全面的安全常识就是在强调人身安全的同时亦必须注意设备、产品的安全。 电气&#xff1a; 怎样预…

IDEA 调试 Ja-Netfilter

首先本地需要有两款IDEA 可以是相同版本&#xff0c;也可以是不同版本。反正要有两个&#xff0c;一个用来调试代码&#xff0c;一个启动。 移除原有ja-netfiler 打开你的ja-netfiler的vmoptions目录&#xff0c;修改其中的idea.vmoptions文件。移除最后一行-javaagent ...参…

分享 .NET EF6 查询并返回树形结构数据的 2 个思路和具体实现方法

前言 树形结构是一种很常见的数据结构&#xff0c;类似于现实生活中的树的结构&#xff0c;具有根节点、父子关系和层级结构。 所谓根节点&#xff0c;就是整个树的起始节点。 节点则是树中的元素&#xff0c;每个节点可以有零个或多个子节点&#xff0c;节点按照层级排列&a…

有效应对服务器遭受CC攻击的策略与实践

分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;尤其是其中的HTTP洪水攻击或称为CC攻击&#xff08;Challenge Collapsar&#xff09;&#xff0c;是当今互联网安全领域的一大挑战。这种攻击通过大量合法的请求占用大量网络资源&#xff0c;导致服务器无法正常响应…

数据可视化入门

文章目录 1. 数据可视化的发展历史1.1 古代与中世纪1.2 18世纪与19世纪1.3 20世纪1.4 21世纪 2. 数据可视化技术基础2.1 数据收集2.2 数据清洗2.3 数据加工 3. 图表库的选型3.1 常见图表库3.2 常用图表库 4. 数据可视化实战4.1 案例&#xff1a;使用Matplotlib和Seaborn进行数据…

AI智能名片S2B2C商城小程序在社群去中心化管理中的应用与价值深度探索

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;社群经济作为一种新兴的商业模式&#xff0c;正逐渐成为企业与用户之间建立深度连接、促进商业增长的重要途径。本文深入探讨了AI智能名片S2B2C商城小程序在社群去中心化管理中的应用&#xff0c;通过详细分析社群去中心化…

【DGL系列】DGLGraph.out_edges简介

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 函数说明 用法示例 示例 1: 获取所有边的源节点和目标节点 示例 2: 获取特定节点的出边 示例 3: 获取所有边的边ID 示例 4: 获取所有信息&a…