前言
上一节中,我们介绍了QML中的定位器(Row,Column,Grid),它们可以用来定位多个组件,让组件按照特定规则进行排列和定位,然而这种方式有一个问题,这些组件必须要被事先设定了尺寸大小,才可以被排列和定位。它们的尺寸通常会被写死,难以实现自适应尺寸的效果。
本节中介绍的Layouts则能够实现这种自适应尺寸的布局,它和QWidget中的QLayout类也有功能上的类似。让我们开始学习吧。
一、了解Layouts
Layouts布局器是一个总称,它通常被分为了RowLayout、ColunmLayout、GridLayout。
我们在帮助文档搜索GridLayout:
翻译一下描述:
如果调整了GridLayout的大小,布局中的所有项目都将被重新排列。它类似于基于小部件的QGridLayout。GridLayout元素的所有可见子元素都将属于布局。如果你想要一个只有一行或一列的布局,你可以使用RowLayout或ColumnLayout。这些提供了更方便的API,并提高了可读性。
默认情况下,项目将根据流属性进行排列。流属性的默认值是GridLayout。从左到右。
如果指定了columns属性,则在自动定位返回到下一行的开头之前,它将被视为布局可以包含的最大列数限制。columns属性仅在流为GridLayout时使用。从左到右。
似乎说的有些云里雾里,我再总结一下:
GridLayout 是 Qt Quick Layouts 模块里的"二维表格"布局器,用来把子项按行 & 列网格摆放,自动计算大小、支持拉伸、对齐、跨行/跨列,几行代码就能做出响应式窗体。
如果你希望构建响应式用户界面------在小屏幕上缩小、在大屏幕上扩展------这将非常有用。比如,如果你在移动设备上运行程序屏幕会缩小,所有内容也会跟着自动缩小,仍能良好显示。而如果是在桌面端运行,界面会扩展并充分利用可用空间。
说的有点啰嗦了,我们直接代码学习。
二、GridLayout代码测试
直接把初始代码放上来:
cpp
import QtQuick 2.14
import QtQuick.Window 2.14
Window {
visible: true
width: 640
height: 480
title: qsTr("QmlLayouts")
Grid{
id: mGridLayoutId
anchors.fill: parent
columns: 3
Rectangle{
id: topLeftRectId
width: 100
height: width
color: "magenta"
Text {
anchors.centerIn: parent
text: "1"
font.pointSize: 20
}
}
Rectangle{
id: topCenterRectId
width: 100
height: width
color: "yellowgreen"
Text {
anchors.centerIn: parent
text: "2"
font.pointSize: 20
}
}
Rectangle{
id: topRightRectId
width: 100
height: width
color: "dodgerblue"
Text {
anchors.centerIn: parent
text: "3"
font.pointSize: 20
}
}
Rectangle{
id: centerLeftRectId
width: 100
height: width
color: "red"
Text {
anchors.centerIn: parent
text: "4"
font.pointSize: 20
}
}
Rectangle{
id: centerCenterRectId
width: 100
height: width
color: "green"
Text {
anchors.centerIn: parent
text: "5"
font.pointSize: 20
}
}
Rectangle{
id: centerRightRectId
width: 100
height: width
color: "blue"
Text {
anchors.centerIn: parent
text: "6"
font.pointSize: 20
}
}
Rectangle{
id: bottomLeftRectId
width: 100
height: width
color: "royalblue"
Text {
anchors.centerIn: parent
text: "7"
font.pointSize: 20
}
}
Rectangle{
id: bottomCenterRectId
width: 100
height: width
color: "yellow"
Text {
anchors.centerIn: parent
text: "8"
font.pointSize: 20
}
}
Rectangle{
id: bottomRightRectId
width: 100
height: width
color: "pink"
Text {
anchors.centerIn: parent
text: "9"
font.pointSize: 20
}
}
}
}

这里使用了定位器中的Grid来实现九宫格,可以看到我们即便设置了anchors.fill: parent
,九宫格也并未如我们所想充满整个屏幕。
第一,这九个矩形全部都被写死了尺寸;第二,组件之间是没有间距的;第三,Grid只会从左上角开始显示组件------所以它们表现出了密集堆积在左上角的样式。
如果我们希望它们能够自适应窗口大小,甚至我们手动拖拽窗口的时候,组件也可以跟着变化大小,我们可以使用GridLayout:
首先,我们需要添加Layout模块,不然你可找不到GridLayout。
cpp
import QtQuick.Layouts 1.12
然后,我们直接将Grid修改为GridLayout,编译运行:
可以看到,整个透明的GridLayout明显是平铺了窗口。我们拖拽窗口改变尺寸看看。
最小缩放到这种程度:
可以得出结论:如果没有进行特殊设置,Layouts的间距默认不是0,源代码里写的是5px。
我们再稍微放大一下窗口:
我添加了几条辅助线,我想要得出的结论是:如果组件被限制了尺寸,而布局器给它分配的空间更大时,该组件会默认按照靠左、垂直居中的方式进行显示。所以如果我们期望组件水平居中、垂直居中,我们需要留意额外的设置实现。
比如,我们往第一个矩形内添加这行代码:
cpp
Layout.alignment: Qt.AlignHCenter

一号矩形此时已经在分配给它的空间里,实现了水平居中,尽管这肉眼上看不太直观。
现在,我们尝试注释掉全部矩形的宽高设置,看会发生怎样的效果?
本以为会九宫格平铺整个窗口,结果缩成了左上角那个小黑点了。
那是因为我们还没给组件们设置尺寸上的伸缩策略。
我们在第一个矩形上添加代码:
cpp
Layout.fillWidth: true
Layout.fillHeight: true
这两句说明了矩形的自适应填充宽高的使能被开启了。此时观察现象:
一号矩形抢占了全部空间,当然,其他矩形还在左上角那个小点上。
我们给全部矩形设置这两句话。
终于,我们想要的效果出来了!
此时拖拽窗口大小也没有问题。
最后,我们继续关注组件的尺寸问题。现在这种0~Max的尺寸虽然看上去很完美,但在某些场合下依旧有局限性。我们可以在保留一定自伸缩性的情况下,再设置组件的最大尺寸和最小尺寸。
cpp
Layout.maximumWidth: 150
Layout.maximumHeight: 150

我们还可以让矩形2占据两列的空间,当然前提是先把矩形3的代码注释掉。
然后,我们对矩形2设置:
cpp
Layout.columnSpan: 2
此时矩形2将在列数上有2个组件的跨度值,效果如下:
同理,我们也可以让矩形4实现行数上的跨度,让4和7合在一起,前提是先把7的代码注释掉。
最后,再介绍一个水平翻转的属性。我们给GridLayout设置:
cpp
layoutDirection: Qt.RightToLeft
可以看到所有组件的排布全部都水平翻转了。
三、总结
不得不说,上述的一系列测试多少有些繁琐了,但我的描述中基本已经包含了Layouts最为核心的常用功能,而这些功能在QWidget中基本上都有对应的实现,属于是Qt框架中用于实现UI布局的非常重要的内容。我甚至认为,只有掌握了这种布局方式,你才能在实际工作中得以很好地实现和还原UI原型图。今后还会继续多加练习和巩固的。
最后,我担心本节的布局器和上一节的定位器会有混淆,所以这里让kimi做了个小表格,希望对理解有帮助:
维度 | 定位器 (Row/Column/Grid) | 布局器 (RowLayout/ColumnLayout/GridLayout) |
---|---|---|
模块 | Qt Quick Basic | Qt Quick Layouts |
子项尺寸 | 固定写死(width/height) | 自动计算(约束 + stretch) |
对齐 | 无,只能自己算 x/y | Layout.alignment: Qt.AlignCenter |
拉伸 | 无 | Layout.fillWidth: true |
间距 | spacing: 10 |
spacing: 10 |
换行/换列 | Grid 支持 | GridLayout 支持 + 自动 flowed |
性能 | 超高(只 setPos) | 稍低(要解线性方程) |
使用场景 | 子项大小固定、列表、工具栏 | 需要响应式、拉伸、对齐的窗体 |