当前位置: 首页 > news >正文

Android onLayout布局流程解析

组件布局流程结论

1.)layout流程始于ViewRootImpl的performLayout()方法,该方法会调用根View(DecorView)的layout()方法进行布局,因为DecorView是ViewGroup(FrameLayout),所以layout流程来到了ViewGroup(其实调用的是父类View类)的layout()方法

  1. )layout()方法负责组件自身的布局,会存储mLeft、mTop、mRight、mBottom 四个值来存储自身的位置,如果布局变化,还会回调onSizeChanged()方法通知我们布局发生了变化。最后layout()方法还会调用onLayout()方法进行子组件的布局。
  2. )View类因为没有子组件,所以View类的onLayout()方法是个空实现,ViewGroup类因为有子组件,但我们自定义的ViewGroup类都有自己的布局规则,所以ViewGroup类的onLayout()方法是个抽象方法,需要子类去实现,所以我们直接继承ViewGroup类一定需要重写onLayout()方法。
  3. )实现ViewGroup的onLayout()方法,我们需要循环所有的子组件,根据我们自己的布局规则,确定他们的位置,然后调用子组件的layout()方法 ,让子组件存储自身的位置,如果子组件是View,则没有后续的布局流程,如果子组件还是ViewGroup,则会重复上述步骤,直到完成所有的布局流程。

View的layout方法

这是一个被final修饰的方法,意味着无法被子类重写。但是内部事件还是调用了View中的layout方法

View中的onLayout是一个空实现的方法,通过layout方法将新的left、top、right、bottom传给onLayout。   官方注释在此视图应该时从布局调用(layout方法执行),此视图(onLayout方法执行)给每个孩子分配一个大小和位置。子类的派生类应该重写此方法并在每个子View上调用布局(layout方法)。   View的子类当然是ViewGroup了,所以ViewGroup更加像是一个View的管理器,用来实现对子View的大小和位置变化进行控制。简单来说View会通过onLayout方法进行确认View的显示位置。

ViewGroup中的onLayout方法是一个抽象方法,所以子类必须实现。并且调用View中的layout方法来确认View的位置和大小。

在layout方法中mLeft、mTop、mRight、mBottom这四个值是用来进行对View位置的摆放和大小的限制,是相对于父控件而言的。

如图白色区域是父View,黑色区域为子View。

针对getWidth()和getMeasuredWidth()进行分析 ,先看下区别

mMeasuredWidth这个值之前在 小试牛刀-onMeasure方法 中介绍过是View的实际大小宽对应的值是mMeasuredWidth,而mMeasuredWidth是通过setMeasuredDimension方法设置进来的。所以在measure方法结束后mMeasuredWidth才会有值,此时调用getMeasuredWidth可以获取对应的值。

在getWidth()中是通过mRight - mLeft的计算返回的结果。而mRight和mLeft这两个值,上面有介绍是通过layout传递过来的。

Layout(源码分析):

Layout的作用就是为整个View树计算实际的位置,而通过刚才对View树的介绍知道,想计算整个View树的位置,就需要递归的去计算每一个子视图的位置(Measure同理)。

而确定这个位置很简单,只需要mLeft,mTop,mRight,mBottom四个值(注意:这4个值是子View相对于父View的值,下面会详细介绍)。

在代码中如何设置这4个值呢? 首先,无论是系统提供的LinearLayout还是我们自定义的View视图,他都需要继承自ViewGroup类,之后必须要做的就是重写onLayout方法(因为在onLayout在ViewGroup中被定义为抽象方法)。

ViewGroup-onlayout:

@Override  
protected abstract void onLayout(boolean changed, int l, int t, int r, int b); 12

onLayout被定义为抽象方法,所以在继承ViewGroup时必须要重写该方法(onMeasure不需要)。另外这个方法也被override标注,所以也是重写的方法,他重写的是其父类view中的onLayout方法。

View-onlayout:

/** * 当这个view和其子view被分配一个大小和位置时,被layout调用。 * @param changed 当前View的大小和位置改变了 * @param left 左部位置(相对于父视图) * @param top 顶部位置(相对于父视图) * @param right 右部位置(相对于父视图) * @param bottom 底部位置(相对于父视图) */  
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}  123456789

注解说:当这个view和其子view被分配一个大小和位置时,被layout调用。所以我们去看看layout中做了什么。(注解没有完全按照英文翻译,并且有省略)

View-layout:

/** * 给View和其所有子View分配大小和位置 * * 这是布局的第二个阶段(第一个阶段是测量)。在这个阶段中,每个父视图需要去调用layout去为他所有的子视图确定位置 * 派生的子类不应该重写layout方法,应该重写onLayout方法,在onlayout方法中应该去调用每一个view的layout */  
public void layout(int l, int t, int r, int b) {  // 将当前视图的左上右下记录为old值(参数中传入的为新的l,t,r,b值)  int oldL = mLeft;  int oldT = mTop;  int oldB = mBottom;  int oldR = mRight;  
​// setFrame方法的作用就是将新传入的ltrb属性赋值给View,然后判断当前View大小和位置是否发生了变化并返回  boolean changed = setFrame(l, t, r, b);  
​if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {  // 调用onLayout回调方法,具体实现由重写了onLayout方法的ViewGroup的子类去实现(后面详细说明)  onLayout(changed, l, t, r, b);  mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;  
​// 调用所有重写了onLayoutChange监听的方法,通知View大小和位置发生了改变  ListenerInfo li = mListenerInfo;  if (li != null && li.mOnLayoutChangeListeners != null) {  ArrayList<OnLayoutChangeListener> listenersCopy =  (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();  int numListeners = listenersCopy.size();  for (int i = 0; i < numListeners; ++i) {  listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);  }  }  }  mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;  
}  12345678910111213141516171819202122232425262728293031323334

在这段代码中我们只要知道:如果视图的大小和位置发生变化后,会调用我们前面分析过的onLayout方法。 对于onLayout方法的最终实现全部依靠我们在自定义ViewGroup类中重写的onLayout去实现。

计算View位置:

在重写的onLayout方法中,唯一的目的就是: 对当前视图和其所有子View设置它们在父视图中具体位置(确定这个位置就依靠mLeft,mTop,mRight,mBottom这四个值) 之前介绍过,mLeft,mTop,mRight,mBottom这四个值表示的是子view相对于父view的位置。下面我贴出我画的图看一下就明白了。

如图,黄色区域是我们的父view,而中间的深色的区域就是我们的子view。 所以对于这个View来说,我列出它相对于父view的各个值是如何计算和相关函数:

mLeft,mTop,mRight,mBottom:

view.getLeft()——mLeft:子View左边界到父view左边界的距离

public final int getLeft() {  return mLeft;  
}  123

view.getTop()——mTop:子View上边界到父view上边界的距离 view.getRight()——mRight:子View右边界到父view左边界的距离 view.getBottom()——mBottom:子View下边距到父View上边界的距离

视图宽高:

视图宽度 view.getWidth();子View的右边界 - 子view的左边界。

public final int getWidth() {  return mRight - mLeft;  
}  123

视图高度 view.getHeight() ;子View的下边界 - 子view的上边界。

public final int getHeight() {  return mBottom - mTop;  
} 123

测量宽高:

view.getMeasuredWidth();measure过程中返回的mMeasuredWidth

public final int getMeasuredWidth() {  return mMeasuredWidth & MEASURED_SIZE_MASK;  
}  123

view.getMeasuredHeight();measure过程中返回的mMeasuredHeight

public final int getMeasuredHeight() {  return mMeasuredHeight & MEASURED_SIZE_MASK;  
}  123

最后介绍一下getWidth/Height和getMeasuredWidth/Height的区别: getWidth,和getLeft等这些函数都是View相对于其父View的位置。而getMeasuredWidth,getMeasuredHeight是测量后该View的实际值(有点绕,下面摘录一段jafsldkfj所写的Blog中的解释). 实际上在当屏幕可以包裹内容的时候,他们的值是相等的,只有当view超出屏幕后,才能看出他们的区别: getMeasuredHeight()是实际View的大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小。 当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的大小

在计算子View在父View中的位置时,主要就是应用上面这几个函数。下面就来看看如何去重写onLayout。

onLayout:

对于重写onLayout的思路和重写onMeasure相同: 如果只需要测量单个View,则单独测量它自己就行。如果需要测量的View其下还有子View,则需要测量其所有的子View。

就以上面的View为例子,他最外面是一个黄色的父View,中间一个居中的深色子View。 我的思路如下: 如果想画出一个View,就要计算它的l,t,r,b值。并传递到onlayout( l, t, r, b )中; mRight = view.getWidth + mLeft; mBottom = view.getHeight + mTop; 所以最后可以用如下形式传入:onlayout( l, t, l+width, t+height );

剩下的任务就只需要知道它的mLeft值,mTop值,加上长、宽值就行了。 长宽值很简单,使用getWidth/Height和getMeasuredWidth/Height都可以。 由于这个View需要居中显示,剩下的问题就是如何计算该View的mLeft值和mTop值。我的思路如下: r(父View的mRight) = mLeft + width + mLeft(因为左右间距一样) b(父View的mBottom) = mTop + height + mTop(因为上下间距一样)

我的代码如下:

@Override  
protected void onLayout(boolean changed, int l, int t, int r, int b) {  
​// 循环所有子View  for (int i=0; i<getChildCount(); i++) {  View child = getChildAt(i);  // 取出当前子View长宽  int width = child.getMeasuredWidth();  int height = child.getMeasuredHeight();  
​// 计算当前的mLeft和mTop值(r,b为传递进来的父View的mRight和mBottom值)  int mLeft = (r - width) / 2;  int mTop = (b - height) / 2;  
​// 调用layout并传递计算过的参数为子view布局  child.layout(mLeft, mTop, mLeft + width, mTop + height);  }  }  123456789101112131415161718

布局文件如下:

<com.gxy.text.CostomViewGroup xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:background="#eee999" >  
​<Button  android:text="ChildView"  android:layout_width="200dip"  android:layout_height="200dip"  android:background="#333444"  android:id="@+id/textView2" />  
</com.gxy.text.CostomViewGroup> 123456789101112

效果图:

总结:

onMeasure和onLayout的大致总结完了,在自定义View的时候最关键的是onLayout,因为无论你如何Measure这个View的大小,最后的决定权永远在onLayout手中,onLayout会决定具体View的大小和位置。当然onMeasure也很重要,有的情况控件的宽高不确定或者需要自定义,这时候需要我们人工Measure它。而在复杂的自定义View时,很多计算也需要在onMeasure中完成,并且些值会记录下来在onLayout中重新使用。

相关文章:

Android onLayout布局流程解析

组件布局流程结论 1.&#xff09;layout流程始于ViewRootImpl的performLayout()方法&#xff0c;该方法会调用根View&#xff08;DecorView&#xff09;的layout()方法进行布局&#xff0c;因为DecorView是ViewGroup(FrameLayout),所以layout流程来到了ViewGroup&#xff08;其…...

浅分析BIG-建筑展示系统

一、主页&#xff08;主要界面&#xff09;重点疑点&#xff08;需要解决&#xff09;1.云平台实时同步。是否可以电脑与hololens2同步或链接&#xff1f;并可以传输信息提醒&#xff1f;一级界面&#xff08;启动界面&#xff09;1.交互式启动激活效果&#xff08;触发按钮旋转…...

模电基础(1) 半导体基础知识

基本内容&#xff1a; 1.本征半导体的基本介绍结构&#xff1b; 2.杂质半导体&#xff1b; 3.PN结的形成&#xff1b; 4.PN结的性质。 1.本征半导体 半导体&#xff1a;导电性能介于绝缘体和导体之间的物质。 本征半导体是纯净的晶体结构的半导体。 纯净→无杂质晶体结构→稳…...

阅读笔记:TF - IDF 原理

今天查阅 TF-IDF 资料&#xff0c;发现百度百科里面提供了一个例子&#xff0c;解释的很清楚&#xff0c;记下来备用。 原文链接&#xff1a;https://baike.baidu.com/item/tf-idf/8816134?fraladdin 例子&#xff1a;在某个一共有一千词的网页中 “原子能”、“的” 和 “应…...

【C语言】float 关键字

&#x1f6a9;write in front&#x1f6a9; &#x1f50e;大家好&#xff0c;我是謓泽&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f3c5;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4…...

Linux 网络编程(实现多路IO转接服务器)

1.select函数实现多路IO转接服务器select函数原型&#xff1a;包含在头文件<sys/time.h>&#xff0c;<sys/types.h>和<unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);作用&#xff1a;确定…...

DC-4 靶场学习

信息搜集&#xff1a; 首先获取靶场ip&#xff0c;和之前一样。 arp-scan -l nmap -sP 192.168.28.0/24然后访问。 发现需要登录。 漏洞分析: 直接用bp爆破&#xff0c;爆破出来密码为happy&#xff0c;登录。 发现执行了命令&#xff0c;抓包。 修改命令可以执行&#xff…...

QML组件

一个QML文件定义了一个独立的、顶级的QML组件。 一个QML组件就是一个模板&#xff0c;被QML运行环境解释来创建一个带有一些预定义行为的对象。 一个独立的QML组件可以运行多次来禅城多个对象&#xff0c;每个对象都可以称为该组件的实例。 例子&#xff1a; 在项目中添加一…...

canvas 学习指南

canvas 学习指南 创建一个 canvas <! DOCTYPE html><html xmlns"http://www.w3.org/1999/xhtml"><head><title></title><meta charset"utf-8" /><script type"text/javascript">window.onload fun…...

【华为OD机试2023】开心消消乐 C++

【华为OD机试2023】开心消消乐 C++ 前言 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能最优),不能保证通过率。 Tips1:机试为ACM 模式 你的代码需要处理输入输出,input/cin接收输入、print/cou…...

学历?能力?

一个面试官愿意看一张有形的总结报告&#xff0c;还是愿意相信看不到的人品&#xff1f;...

使用ECharts打造一个数据可视化面板

使用ECharts打造一个数据可视化面板1. 使用技术2. 案例适配方案3. 基础设置4. header 布局5. mainbox 主体模块6. 公共面板模块 panel7. 柱形图 bar 模块&#xff08;布局&#xff09;8. 中间布局9. ECharts 介绍10. ECharts 体验11. ECharts 基础配置12. 柱状图图表&#xff0…...

【论文简述】PVSNet: Pixelwise Visibility-Aware Multi-ViewStereo Network(arxiv 2020)

一、论文简述 1. 第一作者&#xff1a;Qingshan Xu 2. 发表年份&#xff1a;2020 3. 发表期刊&#xff1a;arxiv 4. 关键词&#xff1a;MVS、3D重建、可见性、代价体、训练策略 5. 探索动机&#xff1a;ETH3D基准测试提供的图像包含强烈的视图变化&#xff0c;这就要求MVS…...

CSS隐藏元素的几种方式以及display、visibility、opacity的区别

CSS隐藏元素的方式首先最通用且最易想到的方法肯定是display、visibility和opacity这三种了display:none设置元素不可见并且连盒模型也不生成&#xff0c;一般用于不占空间的隐藏元素。display属性规定元素应该生成的框的类型&#xff0c;当其值为“none”时可以规定元素不生成…...

【Java|golang】1487. 保证文件名唯一---golang中string方法的坑

给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹&#xff1a;在第 i 分钟&#xff0c;新建名为 names[i] 的文件夹。 由于两个文件 不能 共享相同的文件名&#xff0c;因此如果新建文件夹使用的文件名已经被占用&#xff0c;系统会以 (k) 的形式为新…...

flstudio21水果language选项中文设置方法教程

编曲是通过DAW&#xff08;数字音频工作站软件&#xff09;完成的&#xff0c;也就是我们常说的宿主软件。现在有很多优秀的宿主软件&#xff0c;例如Cubase、Studio One、FL Studio等。 FL Studio是一款功能强大的音乐制作软件&#xff0c;也被称为FruityLoops。目前已经推出…...

Ubuntu中安装StaMPS

Ubuntu中安装StaMPS0 StaMPS简介1 首先安装好MATLAB&#xff0c;安装一些依赖工具包2 安装StaMPS2.1 下载StaMPS安装包2.2 安装2.3 配置环境2.4 matlab中的路径设置0 StaMPS简介 官网&#xff1a;https://homepages.see.leeds.ac.uk/~earahoo/stamps/ A software package to e…...

Spring Security 实现自定义登录和认证(1)

1 SpringSecurity 1.1 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>1.2 编写配置类 在spring最新版中禁用了WebSecurityConfigurerAdapter…...

Linux 进程:辨析wait与waitpid

目录一、wait二、waitpid(1)参数&#xff1a;pid(2)参数&#xff1a;status(3)参数&#xff1a;options(4)返回值wait 与 waitpid 这两个函数的作用是&#xff1a;等待子进程退出&#xff0c;在子进程退出后释放子进程资源&#xff0c;防止子进程变成僵尸进程。但准确的说&…...

移除元素(每日一题)

目录 一、题目描述 二、题目分析 2.1 方法一 2.1.1 思路 2.1.2 代码 2.2 方法二 2.2.1 思路 2.2.2 代码 一、题目描述 题目链接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...