默认的滚动条样式不满足要求,就得自己手动实现一个自定义样式的滚动条,必须重写 background 和 contentItem 属性,即实现自定义的背景、前景组件。
另一种情况是内容根本不在传统的滚动视图(ScrollView、Flickable)里,但滚动条也要能用。这时直接实例化 ScrollBar,用 position 实现内容偏移。
自定义滚动条样式

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Flickable {
id: flickable
Layout.fillWidth: true
Layout.fillHeight: true
clip: true // 防止内容超出边界
contentWidth: contentRect.width
contentHeight: contentRect.height
ScrollBar.vertical: ScrollBar {
id: vBar
width: hovered || pressed ? 14 : 10
policy: ScrollBar.AsNeeded
Behavior on width {
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
}
// 轨道:内嵌浅槽
background: Rectangle {
implicitWidth: 10
radius: width / 2
color: "#1296FF"
opacity: 0.10
border.width: 1
border.color: "#2E1296FF"
}
// 滑块:胶囊形 + 主题色渐变,悬停时加宽提亮
contentItem: Rectangle {
implicitWidth: 8
radius: width / 2
gradient: Gradient {
GradientStop {
position: 0.0
color: vBar.pressed ? "#0A6FB8" : vBar.hovered ? "#4DB3FF" : "#8FCEFF"
}
GradientStop {
position: 1.0
color: vBar.pressed ? "#0A7ACC" : vBar.hovered ? "#1296FF" : "#5FB9FF"
}
}
opacity: vBar.pressed ? 1.0 : vBar.hovered ? 0.95 : 0.8
Behavior on opacity {
NumberAnimation { duration: 150 }
}
}
}
Rectangle {
id: contentRect
width: 500
height: 500
color: "#3498db"
Text {
text: "Scrollable Content"
anchors.centerIn: parent
font.pointSize: 20
color: "#fff"
}
}
}
}
}
自定义滚动条外观就两块:background 画轨道,contentItem 画滑块。
轨道是一条半透明的圆角槽,滑块窄一点嵌在里面,用渐变色填充。滚动条的 width 属性绑定了 hovered(滑过) 和 pressed(按压) 状态。
适用场景:品牌色滚动条、深色主题、需要悬停反馈的列表。
自定义滚动逻辑
直接将滚动条挂在普通 Rectangle 上,推动文字平移。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Rectangle {
id: frame
Layout.fillWidth: true
Layout.fillHeight: true
border.width: 1
border.color: "#ccc"
clip: true
Text {
id: content
text: "ScrollBar In Rectangle"
font.pixelSize: frame.height
x: -hbar.position * width
y: -vbar.position * height
rightPadding: 20
bottomPadding: 20
}
ScrollBar {
id: vbar
hoverEnabled: true
active: hovered || pressed
orientation: Qt.Vertical
size: frame.height / content.height
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 1
}
ScrollBar {
id: hbar
hoverEnabled: true
active: hovered || pressed
orientation: Qt.Horizontal
size: frame.width / content.width
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 1
}
}
}
}
ScrollBar 不只能附加在传统的滚动视图中,也可以当普通组件直接创建。
该模式下核心是两个值:size 是滑块占轨道的比例,算法很简单,可视区高度除以内容总高度;position 是滑块当前位置,取值 0 到 1。
内容的 x、y 坐标绑成 -position * 宽高。滑块往右拖,内容就往左移,方向正好相反,所以前面要加个负号。
active: hovered || pressed 的意思是:平时滚动条淡下去,鼠标碰上或按住时才亮起来。hoverEnabled: true 是悬停检测的开关,不开的话 hovered 永远是 false。水平和垂直滚动条分别锚到容器的边上,位置就固定了。
这种写法的好处是轻量化,不用引入 Flickable,滚动逻辑完全自己说了算。代价是所有滚动行为都得手动接,包括鼠标滚轮、惯性滑动这些 Flickable 自带的能力。
适用场景:自绘视口、只读取滚动位置的场景。
两个方案怎么选
| 需求 | 用哪个 |
|---|---|
内容已在 Flickable / ScrollView 里,只改外观 |
附加 ScrollBar,重写 background / contentItem |
内容不在 Flickable 里,要自己接滚动逻辑 |
直接实例化 ScrollBar,绑 size 和 position |
已验证环境:
- Qt 版本:Qt 6.8.2 、Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_scrollbar