Qt Quick 与 QML(五)qml中的布局
QML布局系统主要分为三大类:锚布局、定位器布局、布局管理器。
一、锚布局(Anchors)
通过定义元素与其他元素或父容器的锚点关系实现精确定位,支持动态调整。
核心特性
属性 | 作用 | 示例 |
---|---|---|
anchors.left | 左边缘对齐目标元素 | anchors.left: parent.left |
anchors.right | 右边缘对齐目标元素 | anchors.right: parent.right |
anchors.top | 顶部对齐目标元素 | anchors.top: parent.top |
anchors.bottom | 底部对齐目标元素 | anchors.bottom: parent.bottom |
组合属性 | ||
anchors.fill | 填充整个目标元素(通常为父容器) | anchors.fill: parent |
anchors.centerIn | 在目标元素中居中 | anchors.centerIn: parent |
边距控制 | ||
anchors.margins | 统一设置四周边距 | anchors.margins: 10 |
*Margin | 单独设置某方向边距(如leftMargin ) | anchors.leftMargin: 15 |
适用场景:需要精确相对定位的UI元素(如表单控件对齐)。
代码示例:
//锚布局Rectangle {// 填充整个区域anchors.left: parent.leftcolor: "#f9e370"width: 300height: 300Rectangle {// 左上角anchors.top: parent.topanchors.topMargin: 5anchors.left: parent.leftanchors.leftMargin: 5color: "#ef2929"width: 50height: 50}Rectangle {// 右上角anchors.top: parent.topanchors.topMargin: 5anchors.right: parent.rightanchors.rightMargin: 5color: "#8ae234"width: 50height: 50}Rectangle {// 左下角anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.left: parent.leftanchors.leftMargin: 5color: "#204a87"width: 50height: 50}Rectangle {// 右下角anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.right: parent.rightanchors.rightMargin: 5color: "#5c3566"width: 50height: 50}Rectangle {// 居中anchors.centerIn: parentcolor: "#ad7fa8"width: 50height: 50}Rectangle {// 中上anchors.top: parent.topanchors.topMargin: 5anchors.horizontalCenter: parent.horizontalCentercolor: "#fcaf3e"width: 50height: 50}Rectangle {// 左中anchors.left: parent.leftanchors.leftMargin: 5anchors.verticalCenter: parent.verticalCentercolor: "#729fcf"width: 50height: 50}Rectangle {// 右中anchors.right: parent.rightanchors.rightMargin: 5anchors.verticalCenter: parent.verticalCentercolor: "#555753"width: 50height: 50}Rectangle {// 中下anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.horizontalCenter: parent.horizontalCentercolor: "#ce5c00"width: 50height: 50}}
运行结果:
二、定位器布局(Positioners)
自动排列子元素,无需手动计算位置,适合规则排列。
四大定位器类型
类型 | 排列方向 | 关键属性 | 特点 |
---|---|---|---|
Row | 水平排列 | spacing | 子项等宽或按内容自适应 |
Column | 垂直排列 | spacing | 子项等高或按内容自适应 |
Grid | 网格排列 | rows /columns | 支持跨行/列,自动换行 |
Flow | 流式排列 | flow | 根据容器宽度自动换行 |
属性
定位器类型 | 核心属性 | 属性说明 | 布局特点 | 典型应用场景 | 代码示例 |
---|---|---|---|---|---|
行定位器 (Row) | spacing | 子项水平间距(像素) | 从左向右水平排列子项 | 水平导航栏、工具栏按钮组 |
|
layoutDirection | 排列方向:Qt.LeftToRight (默认)或Qt.RightToLeft (从右向左) | 支持反向布局 | RTL语言界面适配 | layoutDirection: Qt.RightToLeft | |
列定位器 (Column) | spacing | 子项垂直间距(像素) | 从上向下垂直排列子项 | 设置菜单、垂直列表 |
|
自动尺寸约束 | 宽度继承最宽子项,高度自适应 | 无需显式设置尺寸 | 动态内容容器 | 默认行为 | |
网格定位器 (Grid) |
| 强制指定行/列数量(默认自适应) | 从左到右、从上到下矩阵排列 | 表单布局、图标网格 |
|
flow | 排列顺序:Grid.LeftToRight (先行后列)或Grid.TopToBottom (先列后行) | 控制填充优先级 | 特殊顺序布局 | flow: Grid.TopToBottom | |
| 独立设置行/列间距(覆盖spacing ) | 支持非均衡间距 | 复杂表格布局 | rowSpacing: 5; columnSpacing: 12 | |
流式定位器 (Flow) | flow | 换行方向:Flow.LeftToRight (水平流)或Flow.TopToBottom (垂直流) | 自动换行/换列 | 瀑布流布局、标签云 |
|
padding | 容器内边距(像素) | 子项与容器边缘的距离 | 带边距的响应式布局 | padding: 15 | |
spacing | 统一控制水平和垂直间距 | 简化间距设置 | 紧凑型布局 | spacing: 10 |
适用场景:规则排列的列表、图标网格或色块组。
代码示例:
//定位器布局Rectangle {// 填充整个区域anchors.right: parent.rightcolor: "#c5fd30"width: 300height: 300//水平Row {anchors.top: parent.topanchors.topMargin: 3anchors.left: parent.leftanchors.leftMargin: 3spacing: 3Repeater {model: 4Rectangle {width: 30height: 30color: {switch(index) {case 0: return "red"case 1: return "green"case 2: return "blue"case 3: return "gray"}}}}}//垂直Column {anchors.bottom: parent.bottomanchors.bottomMargin: 3anchors.left: parent.leftanchors.leftMargin: 3spacing: 3Repeater {model: 4Rectangle {width: 30height: 30color: {switch(index) {case 0: return "red"case 1: return "green"case 2: return "blue"case 3: return "gray"}}}}}//网格Grid {anchors.top: parent.topanchors.topMargin: 3anchors.right: parent.rightanchors.rightMargin: 3columns: 3spacing: 3Repeater {model: 14Rectangle {width: 30height: 30color: getRectColor()function getRectColor() {if (Positioner.index % 4 === 0)return 'red'else if (Positioner.index % 4 === 1)return 'green'else if (Positioner.index % 4 === 2)return 'blue'elsereturn 'gray'}}}}//流式Flow {anchors.bottom: parent.bottomanchors.bottomMargin: 3anchors.right: parent.rightanchors.rightMargin: 3width: 162spacing: 3Repeater {model: 14Rectangle {width: 30height: 30color: getRectColor()function getRectColor() {if (Positioner.index % 4 === 0)return 'red'else if (Positioner.index % 4 === 1)return 'green'else if (Positioner.index % 4 === 2)return 'blue'elsereturn 'gray'}}}}}
运行结果:
三、布局管理器(Layout Managers)
提供响应式布局能力,需导入QtQuick.Layouts
模块,支持动态拉伸和约束控制。
核心类型及功能
类型 | 方向 | 特有附加属性 | 功能 |
---|---|---|---|
RowLayout | 水平 | Layout.fillWidth | 子项按比例填充剩余宽度 |
ColumnLayout | 垂直 | Layout.preferredHeight | 控制子项高度优先级 |
GridLayout | 网格 | Layout.rowSpan | 支持跨行/列布局 |
StackLayout | 堆叠 | currentIndex | 类似选项卡切换显示不同子项 |
属性
布局类型 | 核心属性 | 属性说明 | 示例代码 |
---|---|---|---|
行布局 (RowLayout) | spacing | 子项水平间距(像素) |
|
Layout.fillWidth | 子项是否填充剩余宽度(true/false) | Rectangle {Layout.fillWidth: true} | |
列布局 (ColumnLayout) | spacing | 子项垂直间距(像素) |
|
Layout.fillHeight | 子项是否填充剩余高度(true/false) | Rectangle {Layout.fillHeight: true} | |
网格布局 (GridLayout) | columns /rows | 强制指定列/行数(默认自适应) |
|
flow | 排列方向:LeftToRight(默认)或TopToBottom | flow: GridLayout.TopToBottom | |
堆栈布局 (StackLayout) | currentIndex | 当前可见子项的索引(默认0) |
|
count | 子项总数(只读属性) | onCountChanged: console.log(count) | |
Layout.fillWidth/Height | 子项默认填充整个布局区域(true) | Rectangle {Layout.fillWidth: false // 禁用默认填充} |
代码示例:
//布局管理器Rectangle {anchors.left: parent.leftanchors.bottom: parent.bottomcolor: "#5ccbf6"width: 300height: 300//水平RowLayout {anchors.top: parent.topanchors.topMargin: 5anchors.left: parent.leftanchors.leftMargin: 5width: 100Rectangle {color: 'red'Layout.fillWidth: trueLayout.minimumWidth: 50Layout.preferredWidth: 50Layout.maximumWidth: 100Layout.minimumHeight: 25Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}Rectangle {color: "green"Layout.fillWidth: falseLayout.minimumWidth: 25Layout.preferredWidth: 100Layout.preferredHeight: 50Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}}//垂直ColumnLayout {anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.left: parent.leftanchors.leftMargin: 5height: 150Rectangle {color: 'red'Layout.fillHeight: trueLayout.minimumHeight: 50Layout.preferredWidth: 75Layout.preferredHeight: 75Layout.maximumWidth: 100Layout.maximumHeight: 100Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}Rectangle {color: 'green'Layout.fillHeight: falseLayout.minimumWidth: 100Layout.preferredWidth: 100Layout.preferredHeight: 50Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}}//网格GridLayout {anchors.top: parent.topanchors.topMargin: 5anchors.right: parent.rightanchors.rightMargin: 5flow: GridLayout.LeftToRightheight: 120columns: 3Rectangle {color: 'red'Layout.columnSpan: 1width: 30height: 30}Rectangle {color: 'gray'Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenterwidth: 30height: 30}Rectangle {color: 'blue'Layout.rowSpan: 2width: 30height: 30}Rectangle {color: 'green'width: 30height: 30}}//堆叠StackLayout {anchors.right: parent.rightanchors.bottom: parent.bottomcurrentIndex: parseInt(textEdit.text)height: 120width: 120Rectangle {color: 'red'}Rectangle {color: 'green'}Text {text: "Text"}Image {source: "file:///home/li/图片/1.png"}}TextEdit {id: textEditanchors.centerIn: parentwidth: 80height: 20text: qsTr("0")}}
运行结果:
四、定位器布局 vs 布局管理器对比表
特性 | 定位器布局(Positioners) | 布局管理器(Layout Managers) |
---|---|---|
核心类型 | Row , Column , Grid , Flow | RowLayout , ColumnLayout , GridLayout |
子元素尺寸调整 | 固定子元素原始尺寸,不自动缩放 | 动态调整子元素大小(填充/约束空间) |
排列方式 | 简单顺序排列(水平/垂直/网格) | 高级自适应排列(支持权重/对齐/跨行跨列) |
响应式布局 | 窗口缩放时元素位置固定不变 | 自动根据容器尺寸调整子项布局 |
附加属性 | 仅基础属性(如spacing ) | 丰富约束属性(fillWidth /alignment /minimumHeight 等) |
适用场景 | 静态元素排列(图标栏、固定菜单) | 动态表单、可伸缩界面、复杂自适应布局 |
五、完整代码示例
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.14Window {visible: truewidth: 640height: 640title: qsTr("布局")//锚布局Rectangle {// 填充整个区域anchors.left: parent.leftcolor: "#f9e370"width: 300height: 300Rectangle {// 左上角anchors.top: parent.topanchors.topMargin: 5anchors.left: parent.leftanchors.leftMargin: 5color: "#ef2929"width: 50height: 50}Rectangle {// 右上角anchors.top: parent.topanchors.topMargin: 5anchors.right: parent.rightanchors.rightMargin: 5color: "#8ae234"width: 50height: 50}Rectangle {// 左下角anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.left: parent.leftanchors.leftMargin: 5color: "#204a87"width: 50height: 50}Rectangle {// 右下角anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.right: parent.rightanchors.rightMargin: 5color: "#5c3566"width: 50height: 50}Rectangle {// 居中anchors.centerIn: parentcolor: "#ad7fa8"width: 50height: 50}Rectangle {// 中上anchors.top: parent.topanchors.topMargin: 5anchors.horizontalCenter: parent.horizontalCentercolor: "#fcaf3e"width: 50height: 50}Rectangle {// 左中anchors.left: parent.leftanchors.leftMargin: 5anchors.verticalCenter: parent.verticalCentercolor: "#729fcf"width: 50height: 50}Rectangle {// 右中anchors.right: parent.rightanchors.rightMargin: 5anchors.verticalCenter: parent.verticalCentercolor: "#555753"width: 50height: 50}Rectangle {// 中下anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.horizontalCenter: parent.horizontalCentercolor: "#ce5c00"width: 50height: 50}}//定位器布局Rectangle {// 填充整个区域anchors.right: parent.rightcolor: "#c5fd30"width: 300height: 300//水平Row {anchors.top: parent.topanchors.topMargin: 3anchors.left: parent.leftanchors.leftMargin: 3spacing: 3Repeater {model: 4Rectangle {width: 30height: 30color: {switch(index) {case 0: return "red"case 1: return "green"case 2: return "blue"case 3: return "gray"}}}}}//垂直Column {anchors.bottom: parent.bottomanchors.bottomMargin: 3anchors.left: parent.leftanchors.leftMargin: 3spacing: 3Repeater {model: 4Rectangle {width: 30height: 30color: {switch(index) {case 0: return "red"case 1: return "green"case 2: return "blue"case 3: return "gray"}}}}}//网格Grid {anchors.top: parent.topanchors.topMargin: 3anchors.right: parent.rightanchors.rightMargin: 3columns: 3spacing: 3Repeater {model: 14Rectangle {width: 30height: 30color: getRectColor()function getRectColor() {if (Positioner.index % 4 === 0)return 'red'else if (Positioner.index % 4 === 1)return 'green'else if (Positioner.index % 4 === 2)return 'blue'elsereturn 'gray'}}}}//流式Flow {anchors.bottom: parent.bottomanchors.bottomMargin: 3anchors.right: parent.rightanchors.rightMargin: 3width: 162spacing: 3Repeater {model: 14Rectangle {width: 30height: 30color: getRectColor()function getRectColor() {if (Positioner.index % 4 === 0)return 'red'else if (Positioner.index % 4 === 1)return 'green'else if (Positioner.index % 4 === 2)return 'blue'elsereturn 'gray'}}}}}//布局管理器Rectangle {anchors.left: parent.leftanchors.bottom: parent.bottomcolor: "#5ccbf6"width: 300height: 300//水平RowLayout {anchors.top: parent.topanchors.topMargin: 5anchors.left: parent.leftanchors.leftMargin: 5width: 100Rectangle {color: 'red'Layout.fillWidth: trueLayout.minimumWidth: 50Layout.preferredWidth: 50Layout.maximumWidth: 100Layout.minimumHeight: 25Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}Rectangle {color: "green"Layout.fillWidth: falseLayout.minimumWidth: 25Layout.preferredWidth: 100Layout.preferredHeight: 50Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}}//垂直ColumnLayout {anchors.bottom: parent.bottomanchors.bottomMargin: 5anchors.left: parent.leftanchors.leftMargin: 5height: 150Rectangle {color: 'red'Layout.fillHeight: trueLayout.minimumHeight: 50Layout.preferredWidth: 75Layout.preferredHeight: 75Layout.maximumWidth: 100Layout.maximumHeight: 100Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}Rectangle {color: 'green'Layout.fillHeight: falseLayout.minimumWidth: 100Layout.preferredWidth: 100Layout.preferredHeight: 50Text {anchors.centerIn: parenttext: parent.width + 'x' + parent.height}}}//网格GridLayout {anchors.top: parent.topanchors.topMargin: 5anchors.right: parent.rightanchors.rightMargin: 5flow: GridLayout.LeftToRightheight: 120columns: 3Rectangle {color: 'red'Layout.columnSpan: 1width: 30height: 30}Rectangle {color: 'gray'Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenterwidth: 30height: 30}Rectangle {color: 'blue'Layout.rowSpan: 2width: 30height: 30}Rectangle {color: 'green'width: 30height: 30}}//堆叠StackLayout {anchors.right: parent.rightanchors.bottom: parent.bottomcurrentIndex: parseInt(textEdit.text)height: 120width: 120Rectangle {color: 'red'}Rectangle {color: 'green'}Text {text: "Text"}Image {source: "file:///home/li/图片/1.png"}}TextEdit {id: textEditanchors.centerIn: parentwidth: 80height: 20text: qsTr("0")}}}
运行结果: