第8章:模型与视图(二)——GridView、TableView、TreeView、自定义模型(C++/JavaScript)
本章概述
上一章我们掌握了ListView和基础模型的用法,这一章将解锁Qt Quick中更强大的视图组件:GridView网格视图、TableView表格视图和TreeView树形视图,同时学会用JavaScript或C++打造自定义模型,实现更灵活的数据管理。掌握这些技能,复杂高效的结构化数据UI就能轻松构建!
GridView(网格视图)
GridView像一个规整的格子架,特别适合展示图片库、图标集这类需要整齐排列的内容。它继承自Flickable,天生支持滚动和轻拂操作,浏览体验顺畅。
核心属性需重点掌握:
- cellWidth/cellHeight:定义每个网格单元的宽高,决定格子尺寸;
- flow:控制布局流向,可选从左到右(GridView.FlowLeftToRight)或从上到下(GridView.FlowTopToBottom);
- layoutDirection:设置整体布局方向,支持左到右或右到左;
- snapMode:开启对齐模式,让项目精准贴合网格,避免错位。
以下是一个水果网格示例,直观展示用法:
import QtQuick 2.15
import QtQuick.Controls 2.15
GridView {
width: 400; height: 300
cellWidth: 100; cellHeight: 100
model: ListModel {
ListElement { name: "苹果"; color: "red" }
ListElement { name: "香蕉"; color: "yellow" }
ListElement { name: "葡萄"; color: "purple" }
// 可继续添加更多数据
}
delegate: Rectangle {
width: GridView.view.cellWidth - 5
height: GridView.view.cellHeight - 5
color: model.color
border.color: "gray"
radius: 8
Text {
anchors.centerIn: parent
text: model.name
font.bold: true
}
}
}
TableView(表格视图)
TableView专门用于展示二维表格数据,比如报表、统计数据等。它需要配合TableModel或自定义模型使用,还能精细控制行、列标题和单元格的可视化样式。
几个关键概念要理清:
- 角色(Roles):模型中每个数据项可包含多个角色,如用于显示的
display、用于编辑的edit,方便不同场景调用数据; - 委托(Delegate):可分别为行、列、单元格定义专属样式,让表格更个性化;
- 选择(Selection):通过附加属性
TableView.selected就能轻松管理单元格的选中状态。
很多人会混淆TableView与其他视图,这里帮你区分:
- TableView vs ListView:ListView是一维列表,TableView是二维表格;TableView需要更结构化的模型,对大数据集的性能优化更出色;
- TableView vs GridView:TableView有明确的行列标题,单元格是行与列的交集;GridView的每个项目独立,更适合展示集合类内容,而TableView主打数据表格展示。
TreeView(树形视图)
TreeView是展示层次化数据的神器,比如文件系统目录、公司组织架构这类有父子层级的数据。但它对模型有特殊要求,必须实现几个核心接口:
rowCount(parent):返回指定父节点下的子节点数量;index(row, column, parent):创建子节点的索引;parent(index):返回指定索引节点的父索引;hasChildren(parent):判断指定父节点是否包含子节点;data(index, role):返回指定索引和角色对应的数据。
TreeView的架构清晰:TreeView负责可视化展示,TreeModel提供层次化数据,Node Delegate定义每个节点的外观,三者配合就能完美呈现树形结构。
自定义模型
当ListModel、XmlListModel等内置模型满足不了需求时,就需要创建自定义模型,这里分JavaScript和C++两种实现方式:
JavaScript自定义模型
通过继承QtObject并实现get、count等必要接口即可,简单易上手,适合快速原型开发。
以下是一个可管理数据数组的JS模型示例,还能通知视图数据变化:
// MyJsModel.qml
import QtQuick 2.15
QtObject {
id: root
property var dataArray: []
function append(item) {
dataArray.push(item);
// 发送信号通知视图更新
root.dataChanged();
}
function get(index) {
return dataArray[index];
}
function count() {
return dataArray.length;
}
signal dataChanged()
}
使用时直接在ListView中引用并初始化数据:
ListView {
model: MyJsModel {
Component.onCompleted: {
for(var i=0; i<10; i++) append({value: i*10})
}
}
delegate: Text { text: modelData.value }
}
C++自定义模型
如果追求高性能和强大功能,建议用C++实现,通常继承QAbstractItemModel或其子类(如QAbstractListModel)。
以下是一个简单的列表模型示例:
// mylistmodel.h
#include <QAbstractListModel>
#include <QVector>
class MyListModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles { ValueRole = Qt::UserRole + 1 };
explicit MyListModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
void addValue(int value);
private:
QVector<int> m_data;
};
// mylistmodel.cpp
int MyListModel::rowCount(const QModelIndex &parent) const {
// 父节点有效则为子节点,返回0;否则返回数据总数
return parent.isValid() ? 0 : m_data.size();
}
QVariant MyListModel::data(const QModelIndex &index, int role) const {
if (!index.isValid() || index.row() >= m_data.size())
return QVariant();
if (role == ValueRole)
return m_data.at(index.row());
return QVariant();
}
QHash<int, QByteArray> MyListModel::roleNames() const {
// 映射角色名,方便QML调用
return { {ValueRole, "value"} };
}
void MyListModel::addValue(int value) {
// 通知视图即将插入行,确保UI同步
beginInsertRows(QModelIndex(), m_data.size(), m_data.size());
m_data.append(value);
endInsertRows();
}
要在QML中使用该模型,需先在C++主函数中注册:
qmlRegisterType<MyListModel>("CustomModels", 1, 0, "MyListModel");
之后在QML中导入CustomModels模块即可使用。
总结与最佳实践
- 选对视图:根据数据结构和展示需求选择:网格内容用GridView,表格数据用TableView,层级数据用TreeView;
- 模型驱动视图:严格遵循模型-视图-委托(MVD)架构,分离数据与UI,提升代码可维护性;
- 性能优化:
- 大数据集优先使用C++模型,性能远优于JavaScript模型;
- 委托中避免复杂计算和过多子组件,减少渲染压力;
- 合理设置
cacheBuffer属性,预加载部分项目,提升滚动流畅度; - 自定义模型选型:复杂数据操作、网络数据绑定或高性能需求优先选C++模型;简单场景用JavaScript模型即可。
数据流转逻辑清晰:数据源(数据库、文件、网络等)→ 模型(ListModel、TableModel、自定义模型)→ 视图(ListView、GridView、TableView),确保数据与UI高效联动。
