布局管理
- 簡單比較兩種界面管理
- 錨點布局實現比例布局
- 布局管理實現比例布局
- 循環依賴問題簡談
在日常打螺絲中,我們偶爾會需要實現界面各組件能按比例放置,自適應各種分辨率的需求。我用錨點和布局都實現過相關界面,記錄下來兩種方式實現的差異。跟大家一起學習。
簡單比較兩種界面管理
特性 | 布局系統 | 錨點系統 |
---|---|---|
排列方式 | 自動流式布局 | 精確相對定位 |
比例控制 | 內置權重分配機制 | 需手動計算百分比 |
嵌套復雜度 | 適合多層嵌套結構 | 適合扁平化結構 |
動態調整 | 自動響應容器尺寸變化 | 需綁定尺寸信號 |
典型應用 | 表單布局/工具欄/等間距排列 | 懸浮元素/固定邊欄/疊加層 |
錨點、排列的簡單介紹
這兩者并不是嚴格分開使用,經常會混用,比如有時候用錨點分區域,然后用Layout布局控制里面的細節,或者用Layout 控制布局,里面用anchors控制小細節。
錨點布局實現比例布局
QML錨點系統(anchors)提供基于相對關系的布局方式,通過元素間的空間關系實現精準定位。比較直觀,使用起來很方便。
常用接口:
anchors.left
: parent.right ,本組件的左邊是父組件的左邊,相對定位設置 ,還有right 、top 、bottom等anchors.centerIn
: parent ,在父組件里正中央 居中anchors.margins
:30, 整體邊距控制 或單獨設置topMargin
,bottomMargin
,leftMargin
,rightMargin
anchors.horizontalCenter
: parent.horizontalCenter, 相對父組件水平居中anchors.verticalCenter
parent.verticalCenter, 相對父組件垂直居中
實現沒有什么約束(或者是我目前的場景沒有遇到), 直接根據相對比例,計算即可。一般這樣的話,根組件(或者依賴的父組件)要確定好大小,或者成為子組件。
property int ratio : 0.2 // 可以獨立成屬性,方便修改管理Item {id: containerwidth: 400; height: 300Rectangle { // 標題(20%)id: headercolor: "lightgreen"anchors {top: parent.topleft: parent.leftright: parent.right}height: container.height * ratio }Rectangle { // 內容(80%)color: "lavender"anchors {top: header.bottombottom: parent.bottomleft: parent.leftright: parent.right}height: container.height * (1-ratio )}
}
布局管理實現比例布局
所有用布局管理器(如 RowLayout、ColumnLayout)管理的組件都會自動擁有Layout.xxxx 相關的屬性,可以設置它們來控制布局實現。我個人比較推薦使用這種方式,當然有不同想法也歡迎評論區討論
常用接口:
Layout.alignment
: Qt.AlignTop, 控制對齊方式, 有水平居中Qt.AlignHCenter 左對齊Qt.AlignLeft 等Layout.fillWidth
: true, 控制是否填滿寬度,高度是fillHeight
Layout.margins
: 16 ,整體邊距控制 或單獨設置leftMargin
/topMargin
等Layout.preferredWidth
: 50, 設定首選寬度是50,minimumHeight
是最小
QT 在Qt 6.2+里提供了一個更直觀的比例分配屬性
Layout.weight
。它不需要手動設置fillXXX
,直接在兄弟組件之間設定即可。
但目前(2025年)Qt 5.6.3+ 商用還是會收費,所以本文推薦的做法在 Qt 5.x 和 QtQuick.Layouts 1.x 中是可用的。
用Layout布局管理實現比例分配有三個要點:
- 按比例分配:同時設置
Layout.fillxxx
: true 和Layout.preferredxxx
,這樣布局里的組件就會以preferredxxx
作為比例因子,在分辨率變時組件相對大小保持不變。 - 固定像素:僅設置
Layout.preferredxxxx
(不設置 fillxxxx)就會變成按給定的值固定屬性 - 避免循環依賴:不要直接綁定 parent.width 或 parent.height,而是通過布局系統的內置機制實現自適應。
什么意思? 給舉兩個例子就明白了:
一,正確按比例分配(低于Qt 6.2) :
兩個chilstool會按照 8:2 的比例分配,且在窗口分辨率變化的情況下,比例依然不變:
如果想進一步細致控制,加大就行: 78 : 22,只需保證所有子項加起來 =1、=10 或者 =100能整除的就行ColumnLayout
{anchors.fill: parentRectangle{ // 標題(20%)color: "white"Layout.fillWidth: trueLayout.fillHeight: trueLayout.preferredHeight: 2 //0.2也行 比例系數 2 / (2+8) /******/}Rectangle{ // 內容(80%)color: "white"Layout.fillWidth: trueLayout.fillHeight: trueLayout.preferredHeight: 8 //0.8也行 比例系數 8 / (2+8)/******/}
}
二,布局的循環分配 :
父引用子,子引用父,導致計算出錯:
這個其實算是我踩的坑,還以為和錨點一樣的用法,被fillxxxx字面意義帶偏了。記錄一下ColumnLayout {anchors.fill: parentRowLayout {id: childlayoutLayout.fillHeight: true Layout.fillWidth: true // 會先考慮子控件的寬Rectangle {id: chilstoolLayout.fillHeight: trueLayout.preferredWidth: parent.width * 0.8 // 又依賴父控件的寬color: "#ffffff"}Rectangle {id: chilstool2Layout.fillHeight: trueLayout.preferredWidth: parent.width * 0.2color: "#ffffff"}}}
循環依賴問題簡談
QML 的布局系統在計算尺寸時遵循以下步驟:
fillWidth
/fillHeight
并不發生在隱式尺寸計算階段。它類似給父控件聲明“我不知道我尺寸如何,但我要占用你剩下的空間” 。 所以在隱式尺寸計算階段,它的尺寸是未確定的,而在隱式尺寸計算階段,又需要獲知子控件的尺寸,若此時子控件又依賴父控件的尺寸,QML尺寸計算就會陷入循環依賴,引發了錯誤。
從分析上能獲知,想破壞循環依賴,其實就是確定好子控件或父控件的尺寸,或者正確設定比例權重分配,打破循環。
- 所以日常打螺絲中,注意布局管理器下的fillxxx 和子項的 parent.width * 0.8不能一起出現即可。