目录
- 头尾与填充
- 演示代码
- 高亮跟随
- 演示代码
- 高亮相关属性
- 工程下载
GirdView 网格除了中间那些格子,首尾还能各挂一块固定内容,顶部放统计、底部放「加载更多」都是常见用法。当前选中的那一项,也可以交给一个独立的高亮组件来标记,而不是写死在 delegate 里。
这篇两个例子分别看这两块。
头尾与填充
顶部是标题加实时计数,底部是「加载更多」按钮,点一次追加一批,加载满后自动变成提示。
演示代码
import QtQuick import QtQuick.Controls import QtQuick.Layouts FadeInAnimation { id: root property int maxCount: 60 ListModel { id: itemModel // 用循环一次性填充初始数据,避免逐条罗列 ListElement Component.onCompleted: { for (var i = 1; i <= 15; i++) append({ "num": i }) } } 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 GridView { id: grid anchors.fill: parent anchors.margins: 10 clip: true model: itemModel cellWidth: 92 cellHeight: 70 cacheBuffer: 500 // 头部:分组标题 + 实时计数 header: Rectangle { width: grid.width height: 44 color: "#E8F3FF" radius: 8 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "全部内容" color: "#1296FF" font.pointSize: 12 font.bold: true } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "共 " + grid.count + " 项" color: "#1296FF" font.pointSize: 11 } } // 尾部:加载更多入口,加载完切换为提示 footer: Item { width: grid.width height: 52 Button { anchors.centerIn: parent width: 160 font.pointSize: 11 enabled: grid.count < root.maxCount text: grid.count < root.maxCount ? "加载更多" : "已全部加载" onClicked: { var step = Math.min(9, root.maxCount - grid.count) for (var i = 0; i < step; i++) itemModel.append({ "num": itemModel.count + 1 }) } } } // 新增项的入场动画:首次填充与「加载更多」追加的项都走这里 add: Transition { NumberAnimation { properties: "x,y"; from: -60; duration: 450; easing.type: Easing.OutCubic } NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 450 } } delegate: Item { required property int num width: grid.cellWidth height: grid.cellHeight Rectangle { anchors.fill: parent anchors.margins: 5 radius: 8 color: "#FFFFFF" border.color: "#e0e0e0" border.width: 1 Text { anchors.centerIn: parent text: num color: "#333" font.pointSize: 12 font.bold: true } } } } } Text { Layout.fillWidth: true text: "共 " + grid.count + " 项 | 点击 footer 的按钮追加内容,header 的计数会同步变化" font.pointSize: 10 color: "#666" } } }header和footer都是Component,网格会在首尾各生成一个实例,生成出来的对象用只读的headerItem、footerItem就能拿到。header 里左边放标题、右边放项数,右边这段文字直接绑grid.count,所以追加数据时计数会跟着变。
footer 里放了一个按钮,点一下用ListModel.append往模型里补 9 项,加满 60 项后按钮自动禁用、文案换成「已全部加载」,这是「加载更多」的标准写法。网格会把 footer 顶到所有项的后面,数据一多它就自然往下走。
入场动画交给了add过渡:最初填充的 15 项和点「加载更多」追加的那批,都属于新加进模型的项,统一走这段从左上滑入并淡入的动画。GridView 还有个populate过渡,只在视图首次创建或模型被整体替换时播放一次;这里是用循环往ListModel里逐条append,属于新增路径,所以用的是add。
适用场景:数据列表的分组标题、带「加载更多」的内容流、需要顶部统计的看板。
高亮跟随
当前项由一块高亮矩形标出来,可以在「视图托管」和「自己补间」两种跟随方式之间切换。
演示代码
import QtQuick import QtQuick.Controls import QtQuick.Layouts FadeInAnimation { ColumnLayout { anchors.fill: parent anchors.margins: 20 spacing: 15 // ... 省略标题组件 TitleSeparator ... // 控制条 RowLayout { Layout.fillWidth: true spacing: 12 CheckBox { id: followsCheck checked: true text: "跟随当前项" font.pointSize: 10 } Label { text: "时长" font.pointSize: 10 } SpinBox { id: durBox from: 0 to: 1200 stepSize: 50 value: 150 enabled: followsCheck.checked font.pointSize: 10 Layout.preferredWidth: 84 } Label { text: "范围" font.pointSize: 10 } ComboBox { id: rangeBox font.pointSize: 10 Layout.preferredWidth: 120 model: ["不限制", "范围生效", "强制范围"] } Item { Layout.fillWidth: true } } // 内容卡片 Rectangle { Layout.fillWidth: true Layout.fillHeight: true color: "#fafafa" radius: 8 border.color: "#e0e0e0" border.width: 1 GridView { id: grid anchors.fill: parent anchors.margins: 10 clip: true focus: true cellWidth: 84 cellHeight: 84 model: 40 highlightFollowsCurrentItem: followsCheck.checked highlightMoveDuration: durBox.value // NoHighlightRange / ApplyRange / StrictlyEnforceRange highlightRangeMode: rangeBox.currentIndex preferredHighlightBegin: 0 preferredHighlightEnd: grid.cellHeight // 跟随模式切换时,高亮实例会被重建 highlight: followsCheck.checked ? managedHighlight : springHighlight delegate: Item { required property int index width: grid.cellWidth height: grid.cellHeight Rectangle { anchors.fill: parent anchors.margins: 5 radius: 8 color: "#FFFFFF" border.color: "#e0e0e0" border.width: 1 Text { anchors.centerIn: parent text: index + 1 color: "#333" font.pointSize: 12 font.bold: true } } MouseArea { anchors.fill: parent onClicked: { grid.currentIndex = index grid.forceActiveFocus() } } } } } Text { Layout.fillWidth: true text: "共 %1 项 | currentIndex:%2 | 点击或方向键切换".arg(grid.count).arg(grid.currentIndex) font.pointSize: 10 color: "#666" elide: Text.ElideRight } } // 交给视图管理几何的高亮 Component { id: managedHighlight Rectangle { color: "#1296FF" radius: 10 opacity: 0.25 } } // 自行管理几何与移动的高亮 Component { id: springHighlight Rectangle { color: "#1296FF" radius: 10 opacity: 0.25 width: grid.cellWidth height: grid.cellHeight x: grid.currentItem ? grid.currentItem.x : 0 y: grid.currentItem ? grid.currentItem.y : 0 Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } } Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } } } } }highlight是一个独立于 delegate 的组件,视图会负责把它挪到当前项的位置。highlightFollowsCurrentItem为true时,这块高亮的尺寸和移动全由视图托管,highlightMoveDuration决定移动耗时;关掉之后视图不再插手,高亮就得自己盯着currentItem走。
按开关切两个不同的Component是这里的做法。托管模式下只给颜色和透明度,位置宽高交给视图;自行跟随模式下把x、y绑到currentItem的坐标上,再挂SpringAnimation补间,移动时带一点回弹。
highlightRangeMode控制当前项是否被限制在视图的某段区间里,preferredHighlightBegin和preferredHighlightEnd就是这个区间的起止。选「强制范围」时,当前项会被顶到区间位置,滚动也跟着它走。
适用场景:需要明显选中态的宫格、用作选择器或菜单的网格、带键盘导航的图片墙。
高亮相关属性
| 属性 | 作用 |
|---|---|
highlight | 当前项的高亮组件 |
highlightFollowsCurrentItem | 是否由视图托管高亮的几何与移动 |
highlightMoveDuration | 视图托管时的移动时长 |
highlightRangeMode | 当前项是否限制在指定区间内 |
preferredHighlightBegin/preferredHighlightEnd | 区间的起止位置 |
工程下载
- 我的 Qt 版本:Qt 6.12
- 操作系统:Windows 11
- GitHub:QML 示例合集V2.0 - qml_gridview