在上一篇文章中介绍了 Pane、Frame、GroupBox 这三个基础容器。这篇继续深入三个更实用的场景:自定义 GroupBox 外观、用 ScrollView 处理溢出内容,以及把多个容器组合成一个带 header/footer 的可滚动页面。
这三个 demo 更接近真实项目:自定义样式解决设计系统不一致的问题;ScrollView 解决内容超出视口的问题;多层嵌套则是日常布局最常见的组合方式。
自定义 GroupBox
默认 GroupBox 的标题和边框样式由当前控件风格决定。如果你的项目用了自定义主题,或者默认风格跟整体 UI 不搭,就需要通过 background 和 label 两个属性手动接管。
background 负责整个 GroupBox 的底框区域,label 负责标题栏。把它们拆开,可以让你分别控制:比如底框用圆角矩形、标题用加粗字体加图标,互不干扰。这比直接套一个 Rectangle 再手动写标题更省事,因为 GroupBox 已经帮你处理了内容边距和标题位置。
演示代码
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: 20
Layout.rightMargin: 20
spacing: 15
GroupBox {
id: control
title: "自定义GroupBox容器"
Layout.fillWidth: true
Layout.preferredHeight: 120
background: Rectangle {
y: control.topPadding - control.bottomPadding
width: parent.width
height: parent.height - control.topPadding + control.bottomPadding
color: "transparent"
border.color: "#ccc"
radius: 4
}
label: Label {
x: control.leftPadding
width: control.availableWidth
text: control.title
elide: Text.ElideRight
}
Label {
anchors.fill: parent
text: "- 通过 background 自定义边框样式\n- 通过 label 自定义标题外观\n- 适合需要特殊样式的场景"
font.pointSize: 10
textFormat: Text.MarkdownText
wrapMode: Text.WordWrap
}
}
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
background接收一个Rectangle。这里把y设为control.topPadding - control.bottomPadding,高度设为parent.height - control.topPadding + control.bottomPadding,目的是让背景矩形对齐到内容区,避免遮住标题label接收一个Label。示例里只是简单显示标题文字,但你可以加Image做图标、改font做样式、或者用RowLayout组合多个元素import QtQuick.Controls.Basic这个导入很重要。Basic 风格给自定义控件留下了最大的可控空间,不会被平台原生风格覆盖
自定义 GroupBox 适合需要统一设计系统的场景。一旦把样式抽成组件,整个项目的分组框都能保持一致。
ScrollView 容器
列表、长文本、动态生成的内容,都很难在一屏内放下。如果手动算高度再切滚动区域,代码会又臭又长。ScrollView 的作用就是接管这部分逻辑:只要内容超出视口,它就自动显示滚动条。
ScrollView 继承自 Pane,所以背景、内边距这些属性都还在。它的内容可以是任何 Item,常见的配合是 ColumnLayout 加 Repeater 做纵向列表,或者 RowLayout 加 Repeater 做横向卡片。
演示代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: 20
Layout.rightMargin: 20
spacing: 15
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 200
border.color: "#ccc"
radius: 4
ScrollView {
anchors.fill: parent
anchors.margins: 12
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ColumnLayout {
spacing: 6
Repeater {
model: ["### ScrollView容器", "**特性:**", "- 继承自 Pane", "- 提供滚动功能", "- 自动处理内容溢出", "- 支持水平和垂直滚动", "- 可配置滚动条策略"]
Label {
required property string modelData
text: modelData
Layout.fillWidth: true
font.pointSize: 10
textFormat: Text.MarkdownText
}
}
}
}
}
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
ScrollBar.vertical.policy: ScrollBar.AsNeeded让垂直滚动条按需显示。如果内容没超出,滚动条不会出现;超出后自动出现。其他可选值有AlwaysOn和AlwaysOffScrollView本身不限制内容类型,但内容最好有确定的大小,否则滚动计算可能出错- 外层套一个固定高度的
Rectangle,用来限定 ScrollView 的显示区域。ScrollView 会基于这个区域决定什么时候该滚动
ScrollView 的长板是“不用算高度”。你把内容丢进去,它自己处理滚动逻辑。
多层嵌套组合
实际项目里很少单独用一个容器,更多是像搭积木一样组合。这个 demo 展示了一个很常见的结构:Page 做顶层页面,header 放标题栏,footer 放状态信息,中间的内容区用 ScrollView 包裹一行 Pane 卡片。
这种结构的好处是职责清晰:Page 管页面骨架,ScrollView 管滚动,Pane 管单个卡片的样式。哪个部分要改,就去改对应的组件,不会影响其他层。
演示代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Page {
Layout.fillWidth: true
Layout.fillHeight: true
header: Rectangle {
width: parent.width
height: 30
color: "#f5f5f5"
border.color: "#ccc"
Text {
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
font.pointSize: 10
text: "Page容器 - 多层嵌套"
}
}
footer: Rectangle {
width: parent.width
height: 25
border.color: "#ccc"
Text {
anchors.centerIn: parent
font.pointSize: 9
text: "已创建 " + controlRepeater.count + " 个卡片"
}
}
background: Rectangle {
color: "#fff"
border.color: "#ccc"
}
ScrollView {
anchors.fill: parent
anchors.margins: 15
ScrollBar.horizontal.policy: ScrollBar.AsNeeded
RowLayout {
spacing: 10
Repeater {
id: controlRepeater
model: 6
delegate: Pane {
contentWidth: 120
contentHeight: 120
background: Rectangle {
color: "#f5f5f5"
border.color: "#ccc"
radius: 4
}
Label {
anchors.centerIn: parent
text: "卡片 " + (index + 1)
font.bold: true
font.pointSize: 10
}
}
}
}
}
}
}
}
关键逻辑解析
这个结构可以拆成三层看:
- Page — 顶层页面容器,提供
header和footer两个插槽。内容区剩下的空间交给子元素 - ScrollView — 放在 Page 的内容区,负责横向滚动。因为卡片是横向排列的,所以启用水平滚动条
- Pane — 作为
Repeater的 delegate,每张卡片都是一个独立的小容器。contentWidth和contentHeight确保卡片大小一致
controlRepeater.count 在 footer 里实时显示卡片数量。Repeater 的 count 属性会随 model 自动更新,比手动维护计数更可靠。
这个模式可以直接复用到很多场景:横向图片画廊、工具栏卡片、文件选择列表等。
运行验证
- Qt Creator 打开
qml_container/CMakeLists.txt - 按
Ctrl+R运行 - 左侧导航切换 “自定义GroupBox”、“ScrollView”、“嵌套组合” 查看效果
- 在 ScrollView 里增加 Repeater 的 model 数量,观察滚动条自动出现
扩展复用方向
- 自定义 GroupBox 的 label 里加图标,做成带图标的设置分组
- Page + ScrollView + Pane 的组合可以改成纵向瀑布流
- 给嵌套 demo 的 Pane 卡片加鼠标悬停效果
- 把多层嵌套结构封装成
CardGallery组件,传入 model 即可复用
已验证环境:
- Qt 版本:Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_container