PathView 不只是简单地把项沿着路径排开,它还提供了吸附、高亮范围控制等属性,能做出更精细的交互效果。当路径不再是直线,而是闭合的圆形时,还能做出环形轮播效果。
这篇文章用两个例子来演示:第一个例子讲吸附对齐效果;第二个例子用 PathAngleArc 画一个整圆,做出环形滚动效果。
吸附对齐
可以通过下拉框切换吸附模式,对比有吸附和没吸附的区别。

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
FadeInAnimation {
id: root
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
// 内容卡片
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#fafafa"
radius: 8
border.color: "#e0e0e0"
border.width: 1
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 2
color: "#e74c3c"
opacity: 0.45
}
PathView {
id: pathView
anchors.fill: parent
anchors.margins: 10
clip: true
focus: true
model: 12
pathItemCount: 4
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
highlightRangeMode: PathView.NoHighlightRange
snapMode: snapCombo.currentIndex === 0 ? PathView.SnapToItem : PathView.NoSnap
path: Path {
startX: 0
startY: pathView.height / 2
PathLine {
x: pathView.width
y: pathView.height / 2
}
}
delegate: Rectangle {
id: panel
required property int index
width: 84
height: 104
radius: 8
color: PathView.isCurrentItem ? "#1976D2" : "#ffffff"
border.width: 1
border.color: PathView.isCurrentItem ? "#0d47a1" : "#dcdcdc"
Text {
anchors.centerIn: parent
text: panel.index + 1
font.pointSize: 18
font.bold: true
color: PathView.isCurrentItem ? "white" : "#8a99aa"
}
MouseArea {
anchors.fill: parent
onClicked: panel.PathView.view.currentIndex = panel.index
}
}
WheelHandler {
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
target: null
onWheel: (event) => {
if (event.angleDelta.y < 0)
pathView.incrementCurrentIndex()
else if (event.angleDelta.y > 0)
pathView.decrementCurrentIndex()
}
}
Keys.onLeftPressed: pathView.decrementCurrentIndex()
Keys.onRightPressed: pathView.incrementCurrentIndex()
}
}
// 控制条
RowLayout {
Layout.fillWidth: true
spacing: 10
Text {
text: "吸附模式"
font.pointSize: 10
color: "#666"
}
ComboBox {
id: snapCombo
Layout.preferredWidth: 180
font.pointSize: 10
model: ["SnapToItem(吸附)", "NoSnap(不吸附)"]
}
Item { Layout.fillWidth: true }
Button {
text: "上一个"
font.pointSize: 10
onClicked: pathView.decrementCurrentIndex()
}
Button {
text: "下一个"
font.pointSize: 10
onClicked: pathView.incrementCurrentIndex()
}
}
RowLayout {
Layout.fillWidth: true
spacing: 16
Text {
text: "当前项:index " + pathView.currentIndex
font.pointSize: 10
color: "#999"
}
Text {
text: "状态:" + (pathView.dragging ? "拖动中" : (pathView.flicking ? "滑行中" : "已静止"))
font.pointSize: 10
color: "#999"
}
}
}
}
吸附功能由snapMode属性控制。PathView.SnapToItem代表松手后自动对齐到最近条目;PathView.NoSnap为关闭吸附,停留在当前位置。
这里highlightRangeMode设置为PathView.NoHighlightRange,如果使用StrictlyEnforceRange,当前项会被固定在preferredHighlightBegin位置,拖动时整体跟随移动,吸附效果不明显。使用NoHighlightRange,条目可以自由滑动,松手后才回弹吸附,直观体现吸附效果。
delegate内添加MouseArea,点击卡片可直接切换到对应条目:通过附加属性PathView.view获取所属PathView实例,修改currentIndex。点击卡片,卡片就滚动对齐到中间,交互更直观。
滚轮切换使用WheelHandler。PathView原生不处理滚轮事件,需要自行实现:滚轮向上切换上一项,向下切换下一项,逻辑和方向键保持一致。
底部状态栏展示当前索引与拖动状态。dragging和flicking属性可实时获取PathView交互状态,便于调试。
适用场景:商品横向浏览、图片选择器、任何需要"对齐到项"的滑动列表。
环形轮播

演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts
FadeInAnimation {
id: root
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
// 内容卡片
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#fafafa"
radius: 8
border.color: "#e0e0e0"
border.width: 1
PathView {
id: carousel
anchors.fill: parent
anchors.margins: 10
clip: true
focus: true
readonly property real ringRadius: (Math.min(width, height) / 2 - 8) / 1.32
readonly property real dotSize: ringRadius * 0.55
model: 8
pathItemCount: 8
snapMode: PathView.SnapOneItem
preferredHighlightBegin: 0
preferredHighlightEnd: 0
highlightRangeMode: PathView.StrictlyEnforceRange
path: Path {
startX: carousel.width / 2
startY: carousel.height / 2 - carousel.ringRadius
PathAngleArc {
centerX: carousel.width / 2
centerY: carousel.height / 2
radiusX: carousel.ringRadius
radiusY: carousel.ringRadius
startAngle: -90
sweepAngle: 360
moveToStart: true
}
}
delegate: Rectangle {
id: tile
required property int index
width: carousel.dotSize
height: carousel.dotSize
radius: width / 2
color: PathView.isCurrentItem ? "#1976D2" : "#bdc3c7"
scale: PathView.isCurrentItem ? 1.15 : 0.85
Behavior on color { ColorAnimation { duration: 200 } }
Behavior on scale { NumberAnimation { duration: 200 } }
Text {
anchors.centerIn: parent
text: tile.index + 1
font.pointSize: carousel.dotSize * 0.28
font.bold: true
color: "white"
}
}
Keys.onLeftPressed: carousel.decrementCurrentIndex()
Keys.onRightPressed: carousel.incrementCurrentIndex()
}
// 圆心信息
Column {
anchors.centerIn: parent
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: carousel.currentIndex + 1
font.pointSize: carousel.dotSize * 0.62
font.bold: true
color: "#1976D2"
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "共 " + carousel.count + " 项"
font.pointSize: carousel.dotSize * 0.19
color: "#999"
}
}
}
// 控制条
RowLayout {
Layout.fillWidth: true
spacing: 12
Switch {
id: autoSwitch
text: "自动轮播"
font.pointSize: 10
}
Item { Layout.fillWidth: true }
Button {
text: "上一个"
font.pointSize: 10
onClicked: carousel.decrementCurrentIndex()
}
Button {
text: "下一个"
font.pointSize: 10
onClicked: carousel.incrementCurrentIndex()
}
}
}
Timer {
interval: 1200
repeat: true
running: autoSwitch.checked
onTriggered: carousel.incrementCurrentIndex()
}
}
环形路径使用PathAngleArc,设置圆心、半径、起始角度和扫角,即可绘制圆弧。示例中sweepAngle: 360绘制完整圆环,startAngle: -90把起点放在圆环正上方(12点方向),第0项就在此处,也是当前项位置。
preferredHighlightBegin和preferredHighlightEnd都设为0,搭配StrictlyEnforceRange,当前项固定在路径起点(圆环顶部)。切换时所有条目沿圆周旋转,转到顶部即为选中项,效果类似旋转轮盘。
圆环半径和圆点大小动态计算,关联视图尺寸。ringRadius取视图宽高较小值,除以2并预留边距,保证圆环完整显示。dotSize按环半径比例计算,窗口缩放时圆点同步变化,避免图形变形、圆点重叠。
圆心放置大号文字,展示当前项序号和总数量。该图层独立于PathView,用anchors.centerIn: parent居中,作为轮盘中心读数。
自动轮播依靠Timer,每1.2秒调用一次incrementCurrentIndex()。Switch开关控制Timer的running属性,开启自动旋转,关闭则停止。
适用场景:转盘抽奖、环形菜单、创意展示页的旋转导航。
已验证环境:
- Qt 版本:Qt 6.8.2、Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_pathview