SwipeView 是 Qt Quick Controls 里做手势翻页的容器,内容横向排开,滑动一下切换到一页,配合 PageIndicator 提示组件能够显示当前在第几页。
这篇介绍2个最简单的demo, SwipeView 怎么用,再额外增加一个页面指示器。
- SwipeView 基础用法 --- 三个页面直接放容器里,左右滑动切换
- 自定义指示器 --- 重写指示器组件,构建特殊效果
SwipeView 基础用法

三个 CustomRect 页面从上到下摞在 SwipeView 里(实际是横向排开),鼠标拖拽或触屏滑动即可翻页,页面底部浮着三个小圆点显示当前位置。
演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
SwipeView {
id: swipeView
anchors.fill: parent
clip: true
CustomRect { description: "This is CustomRect1" }
CustomRect { description: "This is CustomRect2" }
CustomRect { description: "This is CustomRect3" }
}
PageIndicator {
id: indicator
anchors.bottomMargin: 20
anchors.bottom: swipeView.bottom
anchors.horizontalCenter: parent.horizontalCenter
count: swipeView.count
currentIndex: swipeView.currentIndex
}
}
}
}
CustomRect 是这个仓库里复用的占位组件,本质是一个 Rectangle,只在正中显示一行描述文字,用来假装"这是一个页面"。你自己写的时候放任何内容都行。
关键逻辑解析
SwipeView 的用法就是"往容器里放页面"。每个直接子项就是一页,容器自带滑动识别、切换动画和惯性滚动。页面数量由子项个数决定,不需要你声明"我有 3 页"。
PageIndicator 自己不知道页数和页码,全靠外部喂 。它是纯粹的"展示型"控件,必须显式绑两个属性:count: swipeView.count 告诉它几个点,currentIndex: swipeView.currentIndex 告诉它亮第几个。SwipeView 负责翻页,PageIndicator 只负责画点,二者通过 currentIndex 通信。
基础写法里指示器只能看不能点 。PageIndicator.interactive 默认是 false,点圆点不会跳页。想让它可点击,下一节会看到 interactive: true 这行开关。
clip: true 记得加。滑动过程中相邻页面会短暂超出 SwipeView 的边界,不裁剪的话内容会画出区域外、盖到旁边的控件上。SwipeView 相关场景把它当成标配。
页面和指示器是两层叠加 :PageIndicator 和 SwipeView 不是父子关系,而是兄弟------SwipeView 占满整个区域,指示器用 anchors.bottom 浮在 SwipeView 底部之上 20 像素。这样圆点永远压在页面内容上层。
自定义指示器

基础版只能显示固定样式的圆点,也不能点。这一版做两件事:把指示器的点换成"小圆点 + 数字",并让它可点击跳页;同时页面从"三个写死的子项"改成 Repeater 生成,配合 Loader 做到只创建当前页和左右相邻页。
演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
SwipeView {
id: swipeView
anchors.fill: parent
clip: true
currentIndex: indicator.currentIndex
Repeater {
model: 4
Loader {
active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem
sourceComponent: CustomRect {
description: "This is CustomRect" + (index+1)
Component.onCompleted: console.log("created:", index)
Component.onDestruction: console.log("destroyed:", index)
}
}
}
}
PageIndicator {
id: indicator
interactive: true // 启用点击跳转
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
bottomMargin: 20
}
count: swipeView.count
currentIndex: swipeView.currentIndex
visible: count > 1
spacing: 10
delegate: Column {
spacing: 10
Rectangle {
width: 12
height: 12
radius: 6
color: index === swipeView.currentIndex ? "#21be2b" : "#e0e0e0"
Behavior on color {
ColorAnimation { duration: 200 }
}
}
Text {
text: index + 1
color: "#21be2b"
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
}
}
关键逻辑解析
delegate 决定指示器每一项长什么样 。PageIndicator 的每一项默认是主题自带的圆点,重写 delegate 就能完全自定义。这里每一项是一个 Column:上面 12×12 的圆点,下面跟着页码数字,形成"○1 ○2 ○3 ○4"的竖排组合。
在 delegate 里用 index === swipeView.currentIndex 判断选中态 。delegate 的 index 从 0 开始,和 currentIndex 对上了就亮绿色,否则灰色。Behavior on color + ColorAnimation 让圆点切换颜色时有 200ms 的渐变,视觉上能看出"选中点在移动",而不是生硬跳变。
interactive: true 一行开启点击跳页 。开启后点任意一项,PageIndicator 会修改自己的 currentIndex。注意这里的同步方向:swipeView.currentIndex: indicator.currentIndex 让页面跟随指示器,同时 indicator.currentIndex: swipeView.currentIndex 又让指示器跟随手势滑动------两边互相引用,值一致时不会触发循环,最终效果就是"手滑页面、点圆点、两边永远同步"。
visible: count > 1 防尴尬。只有一页时圆点没有存在意义,条件为假自动隐藏。
页面数量多时用 Repeater + Loader 按需创建 。SwipeView 的全部子项都会被实例化,如果直接写 4 个 CustomRect 或者用 Repeater 平铺,启动时所有页面一次建完。改法是在 Repeater 里放 Loader,用 active 控制"当前页 + 前一页 + 后一页"才真正创建组件------滑动动画只需要相邻页在场,更远的页面等滑到附近再建,滑远了就销毁。demo 里的 console.log 就是让你在控制台看 created/destroyed 的先后,验证懒加载生效。
什么时候该用哪种写法
| 基础版 | 自定义版 | |
|---|---|---|
| 页面写法 | 子项直接写死 | Repeater + Loader 懒加载 |
| 页面数量 | 少、固定 | 多、可程序化生成 |
| 指示器外观 | 主题默认圆点 | 自定义 delegate |
| 能否点击跳页 | 否(interactive 默认 false) |
是 |
页面少且固定,直接把子项写进 SwipeView 最直观;页面多、结构重、想省启动开销,就换 Repeater + Loader 的懒加载写法。指示器想要"点哪跳哪"或改外观,一律重写 delegate 并开 interactive。
运行验证
- Qt Creator 打开
qml_swipeview/CMakeLists.txt,按Ctrl+R运行; - 左侧点「SwipeView 与指示器」,拖拽滑动看三个页面切换、底部圆点跟随;
- 点「自定义指示器」,滑动时看控制台 created/destroyed 日志(只有相邻页会被创建),再点数字圆点直接跳页,观察圆点绿色渐变过渡。
扩展复用方向
- 页面改成
Image就是图片轮播,改成Loader加载外部组件文件就能做"懒加载标签页"; - 指示器圆点想换成进度条、缩略图甚至文字标签,都从改
delegate入手; interactive: true后想监听用户主动跳页(区分手势翻页和点击跳页),在onCurrentIndexChanged里做埋点即可。
已验证环境:
- Qt 版本:Qt 6.8.2 / Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_swipeview