網站首頁 編程語言 正文
布局原理
每個組件在渲染之前的布局過程具體可分為兩個線性過程。首先從組件頂部向下傳遞布局約束,然后從底部向上傳遞布局信息。
這兩個線性過程會在元素樹所引用的RenderObject樹中完成,并且最終的布局信息將保存在RenderObject中。因此,當重新構建組件時,如果元素和RenderObject能夠復用,那么同樣可以使用和上次一樣的布局信息。這種單向傳遞和保存信息的方式是Flutter布局性能優于其他框架的重要原因之一。
RenderObject樹由一個個RenderObject組合而成。當Element實例掛載到元素樹上后,就會調用組件的createRenderObject()方法生成對應的RenderObject。由于RenderObject樹被元素樹引用,并且主要任務就是幫助Element實例做具體的渲染工作,因此RenderObject樹也常稱為元素樹的子樹。
每個RenderObject會被元素持有,并且在組件重建后會盡量復用,每當元素中的狀態發生改變時,就會調用組件的updateRenderObject()方法更新渲染對象,屏幕上的值最終得以更新。
RenderObject類中提供了很多與布局密切相關的對象和方法,如下所示。
- constraints對象,從父組件中傳遞過來的約束對象。
- parentData對象,從子組件傳遞來的具體布局信息。
- performLayout()方法,負責具體的布局邏輯。
- paint方法,繪制組件和它的子組件。
RenderObject是一個抽象類,每種Element都會指向不同類型的渲染對象,繼承自RenderOject的兩個主要的類是RenderBox和RenderSliver,它們分別使用盒子協議和滑動協議來做布局工作。基于這兩個類,Flutter還提供了許多處理特定場景的渲染類,如RenderShiftedBox和RenderStack等。
布局約束 1 —— 盒子協議
box protocol 用于靜態布局
父節點傳遞給其子節點的約束是一個BoxConstraints對象。這種約束對象內部有4個屬性,分別用來規定每個子節點的最大和最小寬度與高度。這種約束會一直向下延伸,子組件也會產生約束,再傳遞給它下面的子組件,這個過程一直延續到組件樹最下面的葉子節點。
比如,父節點傳入了MinWidth=150像素,MaxWidth=350像素,MinHeight=100像素,MaxHeight=double.infinity(盡可能大)的BoxConstraints對象。
子組件接收到這個約束后,便會根據BoxConstraints對象取得上圖中指定范圍內的值,即寬度介于150~350像素,高度大于或等于100像素。當取得具體的值之后,就會將值向上傳遞給父組件,父組件會根據這些信息確定子組件的位置,再對它做具體的布局操作,這樣就達到了父子組件間的布局通信。
組件接收到盒子布局約束后,會分3種情況設置寬度和高度:
- 盡可能大地擴展寬度和高度,即總是取布局約束的最大值,如Center、ListView組件
- 組件會盡量選擇最小值并與它的子組件(如Opacity)的最大寬度、高度相等。
- 像Text、Image這類屬于葉子節點的組件會以固定尺寸渲染。
自定義盒子布局約束
使用 ConstrainedBox 組件
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 150.0, // 最大寬度
maxHeight: 70.0, // 最大高度
),
child: Container(
color: Colors.lightBlue,
),
)
傳遞給子組件Container一個最大寬度為150.0像素、最大高度為70.0像素的盒子約束。由于Container在沒有子組件的情況下會根據布局約束盡可能大擴展寬度和高度,因此Container的寬度會自動設置為150像素,高度會自動設置為70像素。
布局約束 2 —— 滑動協議
sliver protocol 用于滾動布局
滾動組件里的每個子組件都被定義為一個sliver組件,父組件傳遞的約束對象為SliverConstraints,該對象不僅記錄了視圖的滾動方向、遺留空間等信息,還為每個Sliver組件提供了它們在滾動布局中的偏移量。
如上圖中的淺黃色的高亮組件,當偏移量為0像素時,在滾動布局邊緣完全展示出來。
所以,滾動布局中的組件分為3種:
- 當前顯示在布局中的組件
- 已經滾動出屏幕的組件
- 還未滾動到屏幕下方的組件。
原文鏈接:https://blog.csdn.net/weixin_41192489/article/details/124624267
相關推薦
- 2022-10-30 詳解Objective?C?中Block如何捕獲外部值_IOS
- 2022-10-28 UpdateLayeredWindow實現任意異形窗口使用詳解_C#教程
- 2023-01-20 python如何求兩數之和及多數之和_python
- 2022-12-19 C++?Boost?Fusion創建異構容器詳解_C 語言
- 2022-12-09 React文件分段上傳實現方法詳解_React
- 2022-07-06 C#中屬性(Attribute)的用法_C#教程
- 2022-06-12 Android開發之保存圖片到相冊的三種方法詳解_Android
- 2023-02-23 關于golang?字符串?int?uint?int64?uint64?互轉問題_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支