qml----Model/View入门(九)Repeater

本文详细介绍了QML中Repeater的使用方法及其与ListView的区别。Repeater能够根据提供的模型批量创建相同类型的Item组件,适用于不同场景,如数字、字符串列表或对象列表等。同时对比了Repeater与ListView在性能上的差异。

  repeater,用于创建多个基于item的组件,通常会用到三个属性,count-创建item的数量,model-指定数据,delegate-待实例化的模型,一般在创建repeater时不显示初始化。另外,还有其它常用属性:
  itemAt(index) 根据索引返回对应的delegate实例
  model 可以取数字(表示创建的数量)、字符串列表、对象列表、ListModel
  虽然repeater可以一次性创建大量的同一类型的Item,但是当量大时,效率就会低下;而listview呢它会创建少部分item,直到用户需要看到它时才会再创建。

  下面是展示代码

  

import QtQuick 2.2
import QtQuick.Layouts 1.1

Rectangle {
    id: root
    width: 320
    height: 480
    color: "#EEEEEE"

    Grid{
        anchors.left: parent.left
        anchors.leftMargin: 4
        anchors.top: parent.top
        anchors.topMargin: 4
        rows: 3
        spacing: 5

        Repeater{
            //model: 8 //数字模式
            model: ["A", "B", "C", "D", "E", "F", "G", "H"]   //字符串模式
//            model:[
//                {"text": "A", "object": "RA"},
//                {"text": "B", "object": "RB"},
//                {"text": "C", "object": "RC"},
//                {"text": "D", "object": "RD"},
//            ]         //对象模式

//            model: ListModel{
//                ListElement{name : "A"; object: "RA";}
//                ListElement{name : "B"; object: "RB";}
//                ListElement{name : "C"; object: "RC";}
//                ListElement{name : "D"; object: "RD";}
//            }         //ListModel模式

            Rectangle{
                width: root.width / 3 - 7
                height: width
                color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)

                Text{
                    anchors.centerIn: parent
                    font.pixelSize: 15
                    font.bold: true
                    color: "red"
                    text: modelData //modelData.name 或者modelData.text
                }
            }
        }
    }
}

 

转载于:https://www.cnblogs.com/SaveDictator/p/8244556.html

<think>我们正在使用qt-mvvm框架展示列表数据。根据之前的讨论,我们知道qt-mvvm是一个专门为复杂科学应用设计的框架,其中SessionModel作为核心数据模型。但是,对于简单的列表数据展示,我们也可以使用更轻量级的ViewModel。不过,根据用户问题,我们直接使用qt-mvvm框架来展示列表数据。在qt-mvvm中,列表数据通常由ListModel或TableModel来处理。但要注意,qt-mvvm提供了自己的模型类,比如ListModel或TableModel,它们继承自SessionModel或者QAbstractListModel/QAbstractTableModel。根据用户之前的问题,SessionModel支持树状结构,但对于列表(一维数组)同样适用。这里,我们以展示一个简单的联系人列表为例,说明如何使用qt-mvvm进行数据绑定。步骤:1.定义模型(Model):通常是一个继承自QAbstractListModel的类,或者直接使用SessionModel(因为它支持动态属性,适用于动态数据)。2.创建视图模型(ViewModel):它是ModelView之间的桥梁,负责将Model的数据暴露给View,并处理视图的交互。3.创建视图(View):在QML中定义界面,使用ListView等控件。由于用户要求示例,我们将给出一个完整的例子。但是,请注意,qt-mvvm的具体实现可能因版本而异。这里我们将基于通用的MVVM模式和qt-mvvm的特性进行说明。假设我们要展示一个联系人列表,每个联系人有姓名和电话号码。###步骤1:定义模型在qt-mvvm中,我们可以使用内置的`ListModel`或`TableModel`,但更常见的是使用`SessionModel`来动态创建模型。不过,对于列表数据,qt-mvvm也提供了`GenericListModel`模板类。但为了简单,我们可以直接使用QAbstractListModel派生。然而,根据引用[1],我们可以使用QAbstractListModel来存储联系人信息。我们也可以使用SessionModel,因为它可以动态添加属性,但这里为了简单,我们先使用传统的QAbstractListModel。但是,如果使用SessionModel,我们可以动态添加行和属性,而不需要预定义角色。这对于动态数据结构非常方便。由于用户特别要求qt-mvvm,我们这里展示使用SessionModel来存储列表数据。注意:SessionModel本质上是树状结构,我们可以将每个联系人作为一个节点,放在同一层级上,形成一个列表。示例代码(C++部分):```cpp#include<QtMvvmCore/CoreApp>#include<QtMvvmQuick/QuickView>#include<QtMvvmDataSync/SyncViewModel>//我们使用SessionModel#include<QtMvvmDataSync/SessionModel>classContactModel:publicQtMvvm::SessionModel{Q_OBJECTpublic:explicitContactModel(QObject*parent=nullptr):SessionModel(parent){//设置节点类型为列表,根节点下的每个子节点代表一个联系人setRootNode(QtMvvm::SessionNode::List);}//添加一个联系人voidaddContact(constQString&name,constQString&phone){//添加一个新的节点到根节点下auto&node=addNode(QString());//使用空路径即添加到根节点下,自动生成节点名node.addProperty("name",name);node.addProperty("phone",phone);}};```但是,这样创建的联系人模型,每个联系人有自己的节点,节点下有两个属性:name和phone。###步骤2:创建视图模型(ViewModel)视图模型将模型数据暴露给视图,并且处理视图的交互(比如添加、删除、修改联系人)。```cppclassContactViewModel:publicQtMvvm::ViewModel{Q_OBJECTQTMVVM_INJECT_PROP(ContactModel*,model,setModel)//注入模型public:explicitContactViewModel(QObject*parent=nullptr);//添加一个命令,用于添加联系人(在QML中可以绑定到按钮)Q_INVOKABLEvoidaddContact(constQString&name,constQString&phone){if(!m_model)return;m_model->addContact(name,phone);}//也可以通过属性将模型暴露给QMLContactModel*model()const{returnm_model;}voidsetModel(ContactModel*model){m_model=model;}private:ContactModel*m_model=nullptr;};//注册类型QTMVVM_REGISTER_VIEWMODEL(ContactViewModel,"ContactViewModel")```###步骤3:创建视图(View,使用QML)在QML中,我们可以使用ListView来展示列表数据。由于SessionModel是树状结构,它提供了`SessionModelNode`类型来访问节点数据。但是,qt-mvvm提供了一个QML插件,其中有一个`SessionModelView`,可以方便地将树形结构展示为列表。这里,我们使用`Repeater`或`ListView`,但需要搭配一个代理(delegate)来显示每个联系人的数据。```qmlimportQtQuick2.15importQtQuick.Controls2.15importQtQuick.Layouts1.15importQtMvvm.Quick1.0asQtMvvmApplicationWindow{visible:truewidth:400height:600//使用ViewModelQtMvvm.ViewModel{id:viewModelviewModel:"ContactViewModel"//之前注册的视图模型名称}//假设我们的SessionModel每个节点代表一个联系人,节点属性有name和phoneColumnLayout{anchors.fill:parentListView{id:listViewLayout.fillWidth:trueLayout.fillHeight:true//我们使用SessionModelView来将SessionModel转换为QML的ListModel//注意:SessionModelView是qt-mvvm提供的一个工具,用于在QML中展示SessionModel//它将根节点的每个子节点作为ListView的一行model:QtMvvm.SessionModelView{sessionModel:viewModel.model//从视图模型中获取SessionModel}delegate:Item{width:listView.widthheight:50RowLayout{anchors.fill:parentText{text:model.node?model.node.value("name"):""//通过节点访问属性Layout.fillWidth:true}Text{text:model.node?model.node.value("phone"):""Layout.fillWidth:true}}}}//添加联系人的输入框RowLayout{TextField{id:nameFieldplaceholderText:"姓名"Layout.fillWidth:true}TextField{id:phoneFieldplaceholderText:"电话"Layout.fillWidth:true}Button{text:"添加"onClicked:{viewModel.addContact(nameField.text,phoneField.text);nameField.clear();phoneField.clear();}}}}}```说明:-在C++中,我们创建了一个`ContactModel`,它继承自`SessionModel`,并设置根节点为列表类型。然后我们通过`addContact`方法添加联系人,每个联系人作为根节点的一个子节点。-在视图模型中,我们注入了`ContactModel`,并提供了一个添加联系人的命令。-QML中,我们使用`SessionModelView`将`SessionModel`适配成ListView可以使用的模型。在代理中,我们通过`model.node`获取到每个节点,然后通过`value`方法获取属性。注意:`SessionModelView`是qt-mvvm提供的一个辅助类,用于在QML中更方便地展示SessionModel。它将每个节点作为一行,然后可以通过代理访问节点的属性。###数据绑定在QML中,我们通过`SessionModelView`自动将SessionModel的数据变更转换为ListView的更新。当我们在C++中调用`addContact`添加联系人时,SessionModel会发出相应的信号,然后`SessionModelView`会更新内部模型,从而刷新ListView。这就是一个简单的使用qt-mvvm展示列表数据的例子。---###注意事项1.**模型所有权**:确保模型的生命周期管理正确,避免内存泄漏。在上面的例子中,我们假设模型是在C++中创建并传递给视图模型的。2.**类型注册**:记得在`main.cpp`中注册C++类型,这样QML才能使用。```cppqmlRegisterType<ContactModel>("Contacts",1,0,"ContactModel");qmlRegisterType<ContactViewModel>("Contacts",1,0,"ContactViewModel");```3.**动态属性**:如果使用SessionModel,我们可以动态添加属性,但上面的例子中我们在`addContact`时固定添加了name和phone属性。如果需要动态添加不同的属性,则需要在代理中处理不同的属性名。###更通用的方式如果不想使用`SessionModel`的树形结构,qt-mvvm也提供了`ListModel`(类似QAbstractListModel的封装)。具体可以查看qt-mvvm的文档。###性能对于大型列表,使用`SessionModel`可能会有性能问题,因为它设计用于树状结构。如果数据量很大,建议使用`ListModel`(QAbstractListModel)或者使用`TableModel`。§§相关问题§§1.如何实现列表数据的筛选和排序?2.qt-mvvm中如何实现分页加载大型列表?3.在SessionModel中如何修改已有节点的属性?</think> ### 使用Qt-MVVM框架展示列表数据的完整方案 在Qt-MVVM框架中展示列表数据需遵循模型-视图-视图模型分离原则,以下是通过数据绑定实现列表展示的完整流程: --- ### 1. **模型层(Model)定义** 创建继承自`QAbstractListModel`的自定义模型类,实现数据存储和角色访问: ```cpp class ContactModel : public QAbstractListModel { Q_OBJECT public: enum Roles { NameRole = Qt::UserRole + 1, PhoneRole }; explicit ContactModel(QObject* parent = nullptr) : QAbstractListModel(parent) {} // 实现虚函数 int rowCount(const QModelIndex& = QModelIndex()) const override { return m_contacts.size(); } QVariant data(const QModelIndex& index, int role) const override { if (!index.isValid()) return {}; const auto& contact = m_contacts.at(index.row()); switch(role) { case NameRole: return contact.name; case PhoneRole: return contact.phone; } return {}; } // 添加数据的方法 void addContact(const QString& name, const QString& phone) { beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_contacts.append({name, phone}); endInsertRows(); } protected: // 注册角色名(QML可见) QHash<int, QByteArray> roleNames() const override { return {{NameRole, "name"}, {PhoneRole, "phone"}}; } private: struct Contact { QString name; QString phone; }; QVector<Contact> m_contacts; }; ``` --- ### 2. **视图模型层(ViewModel)** 创建视图模型作为模型与视图的桥梁: ```cpp class ContactViewModel : public QObject { Q_OBJECT Q_PROPERTY(ContactModel* model READ model CONSTANT) public: explicit ContactViewModel(QObject* parent = nullptr) : QObject(parent), m_model(new ContactModel(this)) { // 初始化测试数据 m_model->addContact("张三", "13800138000"); m_model->addContact("李四", "13900139000"); } ContactModel* model() const { return m_model; } // 添加新联系人的命令 Q_INVOKABLE void addNewContact() { m_model->addContact("新联系人", "未设置号码"); } private: ContactModel* m_model; }; ``` --- ### 3. **视图层(View)实现(QML)** 使用QML的ListView组件绑定数据: ```qml import QtQuick 2.15 import QtQuick.Controls 2.15 ListView { id: contactList width: 300; height: 400 model: viewModel.model // 绑定到ViewModel delegate: ItemDelegate { width: contactList.width height: 60 Row { spacing: 10 anchors.centerIn: parent Text { text: model.name; font.bold: true } // 直接访问角色名 Text { text: model.phone } } } // 添加新联系人按钮 Button { anchors.bottom: parent.bottom text: "添加联系人" onClicked: viewModel.addNewContact() // 调用VM命令 } } ``` --- ### 4. **初始化绑定** 在main.cpp中完成最终装配: ```cpp int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); ContactViewModel viewModel; QtQuickView view; view.setSource(QUrl("qrc:/main.qml")); // 关键绑定:将ViewModel注入QML上下文 view.engine()->rootContext()->setContextProperty("viewModel", &viewModel); view.show(); return app.exec(); } ``` --- ### 核心机制解析 1. **数据绑定流程** $$ \text{View (QML)} \xrightarrow{\text{data binding}} \text{ViewModel} \xrightarrow{\text{property access}} \text{Model} $$ 当模型数据变化时,通过`dataChanged()`信号自动刷新视图[^2]。 2. **双向绑定** 编辑联系人示例(在delegate中添加): ```qml TextField { text: model.phone onTextChanged: model.phone = text // 双向绑定 } ``` 3. **动态更新原理** 通过`beginInsertRows()`/`endInsertRows()`通知视图更新布局 删除操作需调用`beginRemoveRows()`/`endRemoveRows()`确保界面同步 --- ### 最佳实践建议 1. **角色管理优化** 使用`Q_ENUM`声明角色避免硬编码: ```cpp Q_ENUM(Roles) // QML中可直接访问枚举 ``` 2. **大型列表优化** - 启用`CacheBuffer`预加载项目 - 对模型分页加载: ```cpp void loadPage(int pageNumber) { ... } ``` 3. **解耦模式** ![](https://i.imgur.com/XnJ8nZ0.png) 严格遵循View不直接操作Model的原则[^4] --- ### 调试技巧 1. 控制台查看绑定状态: ```bash QML_DEBUG=true ./app ``` 2. 检测绑定错误: ```qml Component.onCompleted: console.log("绑定状态:", Boolean(contactList.model)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值