!");
}
}
}
}
}
这样就得到了一个列表渲染。此时渲染出来的列表在注册到上下文的时候就已经确定,列表项Name动态的修改会体现到界面上,但是动态增删数组元素不会发生变化(因为 QList 本身并没有 NOTIFY 接口)。
注册类型到QML上下文中
当前的 Name 类已经完备的实现了注册到 QML 上下文中的接口,所以可以在 C++ 中调用
qmlRegisterType
("com.myapp.name", 1, 0, "Name");
//包名,大版本号,小版本号,类型名
进行注册,在 QML 文件中加入引用后就可以直接使用 Name 类以及其属性。
import com.myapp.name 1.0
Name {
data: "Foo"
}
封装一组数据暴露给 QML
与直接放入 QList 不同,通过更高级的封装可以实现动态绑定的列表渲染。避免使用QQmlListProperty这个方法,文档有问题且相关资料少
线性表封装为抽象列表类型
朴素的 C++ 线性表类型(数组或者 Vector 模板类等等)通过封装就可以成为被 QML 直接访问的 Model。封装成的类可以是继承自 QAbstractListModel 或者更复杂的 QAbstractTableModel。关键在于继承后实现几个作为 QML 调用接口的虚函数(完整的虚函数表参照文档):
/*必须实现的虚函数*/
int rowCount(const QModelIndex &parent) const;//返回数据行数
QVariant data(const QModelIndex &index, int role) const;//返回根据index和role请求的数据
QHash
roleNames() const;//返回数据别名
通过实现更多的虚函数可以完成更复杂的面向 QML 功能。
抽象类型定义
假定我们有一个 QList 内部的每个元素都是 Name 类型,注意 Name 已经完成了对 QML 访问所必须的封装。于是一个可在QML中渲染的 Name 类型的线性表封装成的类应该长这样:
/*namelist.h*/
#include
#include
#include
#include "name.h" class NameList : public QAbstractListModel { Q_OBJECT public: enum datatype { type1 = 0 }; NameList(QObject *parent); NameList(){ addName("test1"); addName("test2"); } /*必须实现的虚函数 供QML引擎调用*/ int rowCount(const QModelIndex &parent) const;//返回数据行数 QVariant data(const QModelIndex &index, int role) const;//返回所求的数据 QHash
roleNames() const;//返回数据别名 /*其他接口*/ Q_INVOKABLE bool pushData(QString a_name); private: QList
_NameList;//被封装的数组 };
首先类内声名了一个枚举类型,每个类型对应数据项中被访问的一个属性。Name 类只有一个 data 属性,所以只定义了一种类型。
然后是rowCount(const QModelIndex &parent),QML引擎查询列表时通过这个函数取得列表项的数量。
QVariant data(const QModelIndex &index, int role)是QML引擎用来访问每个列表项的接口,访问的时候会通过index表明索引,role表明查找的属性(对应枚举类型datatype)。
QHash
roleNames()返回role的别名(暂时不是特别重要)。
抽象类型实现
/*namelist.cpp*/
#include
#include
#include "namelist.h" NameList::NameList(QObject *parent) { } int NameList::rowCount(const QModelIndex &parent) const { return _NameList.count();//返回私有列表数据量 } QVariant NameList::data(const QModelIndex &index, int role) const { qDebug() << role; int row = index.row();//index包含.row()和.count()等属性 return QVariant::fromValue(_NameList.at(row));//数据项包装成QVariant返回 } QHash
NameList::roleNames() const { QHash
d; d[datatype::type1] = "Foo";//给tpye1设置别名 return d; } bool NameList::pushData(QString a_name) { Name* cache = new Name(a_name); beginInsertRows(QModelIndex(), _NameList.count(), _NameList.count()); _NameList.append(cache); endInsertRows(); return true; }
列表被封装以后,在进行增删的时候需要先调用beginInsertRows(QModelIndex, int , int),第一个参数对应 Model 数据,通过QModelIndex()得到这个Model的虚拟rootItem;后两个参数代表所改动的行数范围:例如在第二行加入3个数据,则两个参数分别是\( (2, 4) \). 修改完成后还要调用endInsertRows()声名修改完毕。
注册到上下文
/*main.cpp*/
NameList theList;//实例化一个类
QQmlApplicationEngine engine;
QQmlContext* rootContex = engine.rootContext();
rootContex->setContextProperty("namelist", &theList);//注册到上下文
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
在 QML 中读取
/*main.qml*/
ListView{
width: parent.width
height: 300
model: namelist //把抽象类作为model
delegate: Rectangle {
height: 30
width: parent.width
c