这篇介绍 QML 里
Slider的几种常见用法:从最基础的一行滑块,到改手柄、加刻度、完全自定义轨道。看完这几个例子,基本能覆盖日常 UI 里 80% 的 Slider 需求。
- Slider 基础 --- 认识
from/to/value/stepSize,再顺手切个垂直方向 - 自定义手柄 --- 用
handle属性把默认手柄换成圆点样式 - 刻度标记 --- 用
Repeater在滑块下面画刻度和数字 - 自定义轨道 --- 用
background属性同时控制背景轨道和已完成进度
Demo 1:Slider 基础
一个最普通的水平滑块,加上一个 Switch 切换水平/垂直方向。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Slider {
id: control
Layout.fillWidth: true
Layout.preferredHeight: 76
from: 0
to: 100
value: 50
stepSize: 5
}
RowLayout {
Layout.fillWidth: true
spacing: 15
Switch {
text: "垂直方向"
font.pointSize: 10
checked: control.orientation === Qt.Vertical
onClicked: {
control.orientation === Qt.Vertical ?
control.orientation = Qt.Horizontal :
control.orientation = Qt.Vertical;
}
}
Text {
text: "当前值: " + control.value
font.pointSize: 10
}
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
Slider 最核心的属性就四个:
from/to:取值范围value:当前值stepSize:每次变化的步长,这里设成 5,拖动时就会按 0、5、10...这样跳
垂直切换靠的是 orientation 属性。默认是 Qt.Horizontal,把它改成 Qt.Vertical 就行。注意垂直时建议给 Slider 一个固定高度,不然 Layout.fillWidth 撑开后看起来会很小。
Demo 2:自定义手柄
默认手柄是个小圆角矩形,如果想改成带中心圆点的样式,就重写 handle。

演示代码
qml
import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Slider {
id: control
Layout.fillWidth: true
from: 0
to: 100
value: 50
stepSize: 5
handle: Rectangle {
x: parent.leftPadding + (parent.horizontal ? parent.visualPosition * (parent.availableWidth - width) : (parent.availableWidth - width) / 2)
y: parent.topPadding + (parent.horizontal ? (parent.availableHeight - height) / 2 : parent.visualPosition * (parent.availableHeight - height))
width: 24
height: 24
radius: 12
color: parent.pressed ? "#f0f0f0" : "#ffffff"
border.color: "#cccccc"
Rectangle {
anchors.centerIn: parent
width: 8
height: 8
radius: 4
color: "#21be2b"
}
}
}
Text {
text: "当前值: " + control.value
font.pointSize: 11
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
handle 是 Slider 开放出来的一个槽位,你可以把整个手柄替换成任意 Item。这里有两个细节容易踩坑:
- 手柄位置不能写死,得用
visualPosition。它是 0~1 的相对位置,乘以availableWidth - width就能算出正确偏移。 - 要兼容水平和垂直方向。
parent.horizontal为 true 时按 x 轴算;垂直时按 y 轴算,x 轴居中。
另外 parent.pressed 可以判断手柄是否被按下,拿来做按下变色很合适。
Demo 3:刻度标记
有些场景需要让 Slider 停在固定刻度上,比如音量 0~100 每 20 一档。这时除了 stepSize 和 snapMode,还要在视觉上标出刻度。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Column {
Layout.fillWidth: true
spacing: 0
height: 40
Slider {
id: slider
width: parent.width
from: 0
to: 100
value: 20
stepSize: 20
snapMode: Slider.SnapAlways
}
Item {
width: parent.width
height: 30
Repeater {
model: 6 // 0到100,每20一个刻度,共6个数字
Column {
x: slider.leftPadding + index * ((slider.availableWidth) / 5) - width/2
spacing: 2
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
width: 2
height: 8
color: "#888"
}
Text {
text: (index * 20).toString()
color: "#888"
font.pointSize: 10
}
}
}
}
}
Text {
text: "当前值: " + slider.value
font.pointSize: 11
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
刻度不是 Slider 自带的,需要自己在滑块下方画。思路是用 Repeater 生成一组刻度和文字,再用 index 计算每个刻度的 x 坐标。
坐标公式:slider.leftPadding + index * (slider.availableWidth / 5) - width / 2
leftPadding是 Slider 左右内边距,从这里开始算才不会偏。availableWidth是 Slider 实际可拖动的宽度。- 除以 5 是因为 0~100 之间有 5 个间隔。
- 最后减
width / 2把刻度组居中到目标点。
snapMode: Slider.SnapAlways 保证松手后一定回到最近刻度,不会停在中间。
Demo 4:自定义轨道
如果想同时改轨道背景、已完成进度和手柄,就得重写 background 和 handle。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Text {
Layout.alignment: Qt.AlignHCenter
text: Math.round(control.value)
color: "#333"
font.pointSize: 12
}
Slider {
id: control
Layout.fillWidth: true
Layout.preferredHeight: 30
from: 0
to: 100
value: 50
background: Rectangle {
x: control.leftPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
width: control.availableWidth
height: 4
radius: 2
color: "#34495e"
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: "#2ecc71"
radius: 2
}
}
handle: Rectangle {
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
width: 20
height: 20
radius: 10
color: control.pressed ? "#13a333" : "#ecf0f1"
border.color: "#2ecc71"
border.width: 2
Behavior on color {
ColorAnimation { duration: 100 }
}
}
}
Item { Layout.fillHeight: true }
}
}
关键逻辑解析
background 里放两层 Rectangle:一层做深色轨道,一层做绿色进度。进度宽度用 control.visualPosition * parent.width,这样拖动时会实时跟随。
手柄的 x 坐标公式和 Demo 2 一样,但因为这里确定是水平滑块,所以可以直接写:
qml
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
Behavior on color 加了一个 100ms 的颜色过渡,按下和松开时颜色变化会更柔和。
运行验证
- 用 Qt Creator 打开
qml_slider/CMakeLists.txt; - 按
Ctrl+R运行; - 左侧导航切换四个 demo,拖动滑块看效果。
扩展复用方向
- 把 Demo 4 的进度条颜色改成渐变,直接用
Gradient实现。 - 在 Demo 3 的刻度上方加当前值气泡,拖动时显示具体数值。
- 给手柄加阴影或放大效果,用
DropShadow或改变scale实现。
已验证环境:
- Qt 版本:Qt 6.8.2、Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_slider