QML 中的 ListView 中的隐藏秘技

QML 中的 ListView 中的隐藏秘技

应该是 ListView 中 delegate 的秘技。

ListView 代理中获取下标

ListView {
    model: 10
    delegate: Text {
        text: "index"+index
    }
}

使用 index 来获取代理在视图中展示的是哪个数据。(只此一条方法)

ListView 代理中获取到一条模型记录的某个字段

ListView {
    width: 400
    height: 400
    model: ListModel {
        ListElement {
            age: 1
        }
        ListElement {
            age: 2
        }
        ListElement {
            age: 3
        }
        ListElement {
            age: 4
        }
    }
    delegate: Text {
        text: age
    }
}

如上所写,只要在 delegate 中写上对应的字段名(角色名)即可。

但是如果模型设置为 JavaScript 数组的时候,上诉写法就会报错了。如下:

ListView {
    width: 400
    height: 400
    model: [
        { "age":1},
        { "age":2},
        { "age":3},
        { "age":4},
    ]
    delegate: Text {
        text: age            // ReferenceError: age is not defined
    }
}

这个时候就需要获取一条模型记录,然后再取对应的字段(角色)了。

ListView 代理中获取一条模型记录

如何在代理中获取 model 的一条记录呢?

这里先说说 ListView::model 所能接受的数据类型吧。

  • int

  • QList<T>

  • QList<QObject*>

  • QListString

  • QAbstractItemModel

  • js array

  • ListModel

大概有七种类型,如果将 model 设置为数字的话,就会进行简单的类型转换,转成成 QList<int> 这种序列类型,并从小到大的进行数据填充。

视图通过代理将模型中数据一个一个进行绘制。那么代理中怎么获取到一条模型记录呢?

且看如下代码:

ListView {
    anchors.fill: parent
    model: [
        {
            "age": 1
        },
        {
            "age": 2
        },
        {
            "age": 3
        },
        {
            "age": 4
        }
    ]
    delegate: Text {
        property var item: modelData
        property int age: item && item.age
        text: age
    }
}

ListView 在实例化代理中的图元时,会为每一个代理图元植入对应下标的 modelData ,这个就是一条模型记录,通过捕获 modelData 这个属性,然后在访问对应字段。

但是在 ListView 设置为 ListModel 的话,就无法使用 modelDate 这个属性了。然后直接给出兼容代码:

 // 本方法不适合在 QAbstractItemModel* 及其子类中使用,除非 QAbstractItemModel* 及其子类有设定 modelData 这个角色
property var item: try {
                       // modelData 适用与 list<QtObject> 类型, js 数组类型,数字等
                       // 注意 QtObject* 除 QAbstractItemModel* 外都会转换成 list<QtObject> 这种类型

                       // console.debug(typeof modelData)
                       return modelData;
                   } catch(e) {
                       return ListView.view.model.get(index);
                   }

上诉代码不能和 QAbstractItemModel* 这个模型配套。

这时需要对 QAbstractItemModel* 的子类做一些功夫。首先设置 modelData 这个角色,然后在 QAbstractItemModel::data 这个函数中返回对应到 modelData 的记录,这个记录可以使用一个结构体存放,或者使用 QJasonObject 存放。

具体代码如下:

CppListModel

#ifndef CPPLISTMODEL_H
#define CPPLISTMODEL_H

#include <QAbstractListModel>

class CppListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit CppListModel(QObject *parent = 0);

    enum Roles{
        NameRole = Qt::UserRole + 1,            // name
        AgeRole,                                // age
        ModelDataRole,                          // modelData
    };

    QHash<int, QByteArray> roleNames() const;
    int rowCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
};

#endif // CPPLISTMODEL_H

实现:

#include "cpplistmodel.h"
#include <QJsonObject>
#include <QtDebug>

CppListModel::CppListModel(QObject *parent):
    QAbstractListModel(parent)
{  }

QHash<int, QByteArray> CppListModel::roleNames() const
{
    QHash<int, QByteArray> roleNames;
    roleNames.insert(NameRole, "name");
    roleNames.insert(AgeRole, "age");
    roleNames.insert(ModelDataRole, "modelData");
    return roleNames;
}

int CppListModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    // 返回固定
    return 5;
}

QVariant CppListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() < 0)
        return QVariant();

    if (index.row() >= 5) {
        qWarning() << "SatelliteModel: Index out of bound";
        return QVariant();
    }

    switch (role)
    {
    case NameRole:
        return QString("this name from cpp");
    case AgeRole:
        return 551;
    case ModelDataRole:
        return QJsonObject {
            { "name", "this name from cpp"},
            { "age", 551 }
        };
    default:
        break;
    }
    return QVariant();
}

在 QML 中使用:

import QtQuick 2.5
import QtQuick.Controls 1.4
import TestModel 1.0

ApplicationWindow {
    visible: true
    width: 360
    height: 640
    title: qsTr("Hello World")

    CppListModel {
        id: cppListModel
        // name, age
    }

    ListView {
        anchors.fill: parent
        model: cppListModel
        delegate: itemDelegate
    }

    Component {
        id: itemDelegate
        Item {

            // 本方法不适合在 QAbstractItemModel* 及其子类中使用,除非 QAbstractItemModel* 及其子类有设定 modelData 这个角色
            property var item: try {
                                   // modelData 适用与 list<QtObject> 类型, js 数组类型,数字等
                                   // 注意 QtObject* 除 QAbstractItemModel* 外都会转换成 list<QtObject> 这种类型

                                   console.debug("typeof modelData", typeof modelData)
                                   return modelData;
                               } catch(e) {
                                   return ListView.view.model.get(index);
                               }

            property string name    : item && item.age
            property string age     : item && item.age
            Text {
                anchors.centerIn: parent
                text: name + age
            }
        }
    }
}

Using C++ Models with Qt Quick Views

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值