默认进度条能用,但放进自己的界面里总觉得差点意思。这篇讲三条改样式的路子:完全不用控件、自己用 Rectangle 画;借用官方 Material 组件做细条;以及任务进度未知时的"不确定动画"。
- 矩形自定义 --- 纯
Rectangle自绘,胶囊外观 + 居中百分比,拖滑块看进度平滑跟随 - Material 风格 --- 用官方
ProgressBarImpl做细条进度,配合开关切换不确定模式 - 不确定动画 --- 进度未知时用条纹滚动动画,表示"正在忙"
Demo 1:矩形自定义
不依赖 ProgressBar 控件,用两个 Rectangle 就把进度条画出来了:一个做底,一个做填充,中间叠一行百分比文字。拖动下面的滑块,填充跟着平滑动画过去。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Rectangle {
id: control
Layout.fillWidth: true
height: 24
property int minimum: 0
property int maximum: 100
property int value: slider.value
border.color: "#ccc"
radius: height / 2
clip: true
Rectangle {
width: ((control.width * (control.value - control.minimum)) / (control.maximum - control.minimum) - 6)
height: parent.height
Behavior on width { SmoothedAnimation { velocity: 1200 } }
anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: 3 }
radius: height / 2
color: "#ff9800"
}
Text {
anchors.centerIn: control
color: "#333"
font.bold: true
text: Math.floor((control.value - control.minimum) / (control.maximum - control.minimum) * 100) + '%'
}
}
Slider {
id: slider
Layout.fillWidth: true
from: 0
to: 100
value: 30
focusPolicy: Qt.NoFocus
}
Text {
text: "进度值: " + control.value
font.pointSize: 11
color: "#666"
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
外层 Rectangle 当容器,自己定义 minimum / maximum / value 三个属性------是的,没用到 ProgressBar,属性名是自己起的,只是碰巧和官方一致,方便理解。
填充宽度是典型的进度换算:
qml
width: (control.width * (control.value - control.minimum)) / (control.maximum - control.minimum) - 6
先算"当前值在范围内占了几成",乘上总宽度就是填充宽度。减去的 6 是给左右各留 3 的边距(配合 anchors.margins: 3),让填充不会贴死边框。
radius: height / 2 是胶囊形:高度 24,圆角半径 12,正好半个高,两头就是半圆。进度条整体比默认样式圆润。
百分比文字直接叠在进度条正中。文字用的是深色,因为橙色的填充在大部分进度下都够亮,深色字反而清楚。
拖动滑块时填充的"追着跑"效果来自 SmoothedAnimation:它和上一篇的 NumberAnimation 一样是给 width 变化加动画,区别是它按固定速度追目标值,拖得快就跟得紧,拖得慢就慢慢滑过去,手感更自然。
Demo 2:Material 风格
Material 风格不用自己画圆角矩形,官方提供了 ProgressBarImpl 这个内部组件,配合主题色,几行代码就是一条带涟漪感的细进度条。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Controls.Material.impl
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 20
// ... 省略标题组件 TitleSeparator ...
ProgressBar {
id: control
Layout.fillWidth: true
value: 0.6
contentItem: ProgressBarImpl {
implicitHeight: 4
scale: control.mirrored ? -1 : 1
color: control.Material.accentColor
progress: control.position
indeterminate: control.visible && control.indeterminate
}
background: Rectangle {
y: (control.height - height) / 2
implicitHeight: 4
color: Qt.rgba(control.Material.accentColor.r, control.Material.accentColor.g, control.Material.accentColor.b, 0.25)
}
}
Switch {
text: "开启不确定模式"
font.pointSize: 11
checked: control.indeterminate
onClicked: control.indeterminate = !control.indeterminate
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
ProgressBarImpl 是 Qt 官方 Material 风格内部用的进度填充组件,把它塞进 contentItem,等于直接借用了官方画好的那层填充:
color用control.Material.accentColor,跟着主题强调色走;progress绑control.position(就是 value 换算后的 0~1 位置);indeterminate透传控件的不确定状态。
background 用了一条极细的 4 高矩形,颜色是主题色的低透明度版(Qt.rgba 把 alpha 压到 0.25),配合 y 居中,形成"底浅条、上深条"的两层结构。这就是 Material 进度条的标志性外观。
底下的 Switch 用来切换 indeterminate。打开后,细条会变成官方的循环滑动动画------这是 ProgressBarImpl 内置的能力,一行动画代码都不用写。下一节的条纹动画就是自己实现这种"不确定"效果。
Demo 3:不确定动画
不知道任务还要多久时(比如"正在加载"),显示具体百分比反而尴尬。这个 demo 用一张条纹蒙版图,在进度条上循环滚动,做出经典的不确定动画。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Controls.impl
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
ProgressBar {
id: control
Layout.fillWidth: true
height: 24
value: 0.6
contentItem: Item {
scale: control.mirrored ? -1 : 1
Rectangle {
height: parent.height
width: (control.indeterminate ? 1.0 : control.position) * parent.width
radius: 2
border.color: "#ccc"
color: "#1296ff"
}
Item {
x: 1; y: 1
width: parent.width - 2
height: parent.height - 2
visible: control.indeterminate
clip: true
ColorImage {
width: Math.ceil(parent.width / implicitWidth + 1) * implicitWidth
height: parent.height
mirror: control.mirrored
fillMode: Image.TileHorizontally
source: "/images/progressmask.png"
color: Color.transparent(Qt.lighter("#1296ff", 1.2), 160 / 255)
visible: control.indeterminate
NumberAnimation on x {
running: control.indeterminate && control.visible
from: -31 // progressmask.png width
to: 0
loops: Animation.Infinite
duration: 750
}
}
}
}
background: Rectangle {
radius: 2
color: "#fff"
border.color: "#ccc"
}
}
Switch {
text: "开启不确定模式"
font.pointSize: 11
checked: control.indeterminate
onClicked: control.indeterminate = !control.indeterminate
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
思路一句话:不确定模式下手动让进度条占满,再在上面盖一层滚动条纹。
底层的蓝色矩形在 indeterminate 开启时宽度直接乘 1.0,也就是铺满整条。条纹是 ColorImage:加载 progressmask.png 这张斜纹蒙版图,fillMode: Image.TileHorizontally 让它横向平铺,color 属性把蒙版的白色区域染成半透明蓝------蒙版图本身只有黑白,真正显示什么颜色由这行决定。
滚动靠一个永不停歇的位移动画:NumberAnimation on x 从 -31 回到 0,循环执行。条纹宽度是"比容器宽一屏"(Math.ceil(...) * implicitWidth),这样从 -31 移到 0 的位移恰好无缝衔接,看起来就是无限循环的条纹爬行。clip: true 保证条纹只出现在进度条内部。
这两个 demo 的 Switch 一开,进度条就从"看得见进度的模式"切到"不知道进度的模式",indeterminate 是 ProgressBar 官方的状态位,自己实现时照用就行。
对比
| 样式 | 实现方式 | 适合场景 |
|---|---|---|
| 矩形自定义 | 纯 Rectangle 自绘 |
外观完全自己掌控,要百分比文字 |
| Material 风格 | ProgressBarImpl + 主题色 |
想快速做出 Material 观感 |
| 不确定动画 | 蒙版图 + 平铺位移 | 进度未知,表示"正在忙" |
运行验证
- 用 Qt Creator 打开
qml_progressbar/CMakeLists.txt; - 按
Ctrl+R运行; - 拖动矩形自定义的滑块看平滑跟随;开 Material 和条纹版的开关,对比两种不确定动画的差异。
扩展复用方向
- 胶囊形进度条换个颜色、加个渐变,就是一套自定义主题。
- 条纹蒙版图可以自己画其他纹理(点状、格纹),
color染色逻辑不变。 - 把三个 demo 的思路混用:Material 细条 + 条纹蒙版,就是带条纹的 Material 不确定进度条。
已验证环境:
- Qt 版本:Qt 6.8.2 / Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_progressbar