1、ListView的使用介绍(Qt QML C++)

540 阅读5分钟

1. ListView简单介绍

** ListView是一个用作列表条目显示的控件工具,在实际开发当中使用的很多,管理数据库列表和显示等等,但是Qt QML自带的ListView显示的数据相对比较简单,不能很好应该与实际开发中复杂的数据结构的显示,其中条目对应的数据主要是来自于Model,应该需要进行管理和设计Model进行实际中的开发运用,而外观显示则是有Delegate进行决定的,这也是QT中Model-View编程框架的使用规则。**

image.png

在Qt-Quick中,数据通过Model-View模式,从显示中分离出来

在QML中,model和view由delegate连接,职责划分如下:

  1. Model提供数据,每个数据项,可能有多个值,例如,每个电话薄条目都有姓名、图片和号码。
  2. 数据排列在一个view中,其中每个项都使用delegate进行可视化。
  3. View的任务是排列delegate,而每个delegate进行可视化。
  4. Delegate知道model的内容以及如何可视化它。
  5. View知道delegate的概念以及如何布置他们。
  6. Model只知道它锁表示的数据。

2、ListView的简单使用

2.1、 简单再QML内部定义代理和Model实现ListView的使用:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    // 实现代理
    Component{
        id: dataDelegate
        Item {
            id: dataItem
            width: parent.width
            height: 32
            MouseArea{
                anchors.fill: parent
                onClicked: dataItem.ListView.view.currentIndex = index
            }
            RowLayout{
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                spacing: 8
                Text {
                    id: indexFirst
                    text: name
                    color: dataItem.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: dataItem.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
                Text {
                    id: indexSecond
                    text: from
                    color: dataItem.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: dataItem.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
                Text {
                    id: indexThird
                    text: message
                    color: dataItem.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: dataItem.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
            }
        }
    }
    ListView{
        id: dataListView
        anchors.fill: parent
        delegate: dataDelegate
        model: ListModel{
            id: dataModel
            ListElement
            {
                name: "韩伟"
                from: "China"
                message: "志不在高,在于不断创造和实现。"
            }
            ListElement
            {
                name: "杰瑞杨"
                from: "Englist"
                message: "I will be successful!"
            }
            ListElement
            {
                name: "金顺"
                from: "Korea"
                message: "有智者立长志,无志者长立志。"
            }
            ListElement
            {
                name: "周炜溟"
                from: "China"
                message: "相信自己,一定能成功。"
            }
        }
        focus: true
        highlight: Rectangle{
            color: "lightblue"
        }
    }
}

运行效果:

image.png

2.2、 结合C++定义QLsitModel「QAbstractListModel」(实现ListView的使用):

创建MyListModel:

image.png

image.png

MyListModel.h代码:
#ifndef MYLISTMODEL_H
#define MYLISTMODEL_H

#include <QAbstractListModel>
class MyData{
public:
    // 使用初始化构造
    MyData(QString n,QString f,QString d):m_name(n),m_from(f),m_dialog(d){
    }
    QString m_name;
    QString m_from;
    QString m_dialog;
};

class MyListModel : public QAbstractListModel
{
    Q_OBJECT
    // 枚举 管理对应的映射
    enum MyRoleName{
        Name = Qt::DisplayRole + 1,
        From,
        Dialog,
    };
public:
    explicit MyListModel(QObject *parent = nullptr);
    // 注册实例
    static MyListModel *getInstance();
    // Header:
    // QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    // 对应管理空间中的ListElement
    QHash<int,QByteArray> roleNames() const override;
private:
    QList<MyData> m_data;
};
#endif // MYLISTMODEL_H

MyListModel.cpp代码:

#include "MyListModel.h"
#include <QDebug>
MyListModel::MyListModel(QObject *parent)
    : QAbstractListModel(parent)
{
    m_data.append(MyData("韩伟","China","志不在高,在于不断创造和实现。"));
    m_data.append(MyData("杰瑞杨","English","I will be successful!"));
    m_data.append(MyData("金顺","Korea","有智者立长志,无志者长立志。"));
    m_data.append(MyData("周炜溟","China","相信自己,一定能成功。"));
}

MyListModel *MyListModel::getInstance()
{
    static MyListModel *ptr_obj = new MyListModel;
    return ptr_obj;
}

//QVariant MyListModel::headerData(int section, Qt::Orientation orientation, int role) const
//{
//    // FIXME: Implement me!
//}

// 元素的个数或者说是model的大小或者长度
int MyListModel::rowCount(const QModelIndex &parent) const
{
    // For list models only the root node (an invalid parent) should return the list's size. For all
    // other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
    if (parent.isValid())
        return 0;

    // FIXME: Implement me!
    return m_data.count();
}

QVariant MyListModel::data(const QModelIndex &index, int role) const
{
    // QModelIndex index是(0,0),对应的是row index和column index
    if (!index.isValid())
        return QVariant();
    if(role == MyRoleName::Name)
    {
        return m_data[index.row()].m_name; // 获取当前MyData的数据
    }
    else if(role == MyRoleName::From)
    {
        return m_data[index.row()].m_from;
    }
    else if(role == MyRoleName::Dialog)
    {
        return m_data[index.row()].m_dialog;
    }
    // FIXME: Implement me!
    return QVariant();
}

QHash<int, QByteArray> MyListModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles.insert(MyRoleName::Name,"name");//枚举类型和类型对应名称
    roles.insert(MyRoleName::From,"from");// 枚举是CPP来进行判断的
    roles.insert(MyRoleName::Dialog,"dialog");// 枚举是CPP来进行判断的
    return roles;
}

main.cpp代码:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "MyListModel.h"
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    // 注册
    engine.rootContext()->setContextProperty("MyListModel",MyListModel::getInstance());
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    // 实现代理
    Component{
        id: dataDelegate
        Item {
            id: dataItem
            width: parent.width
            height: 32
            MouseArea{
                anchors.fill: parent
                onClicked: dataItem.ListView.view.currentIndex = index
            }
            RowLayout{
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                spacing: 8
                Text {
                    id: indexFirst
                    text: name
                    color: dataItem.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: dataItem.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
                Text {
                    id: indexSecond
                    text: from
                    color: dataItem.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: dataItem.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
                Text {
                    id: indexThird
                    text: dialog
                    color: dataItem.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: dataItem.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
            }
        }
    }
    ListView{
        id: dataListView
        anchors.fill: parent
        delegate: dataDelegate
        model: MyListModel
        focus: true
        highlight: Rectangle{
            color: "lightblue"
        }
    }
}

2.3、 对ListView的model数据进行增加和删除

import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0



Window {
    id: window
    visible: true
    width: 600
    height: 480
    title: qsTr("Hello World")

    Rectangle {
        anchors.fill: parent
        color: "#EEEEEE"

        Component {
            id: phoneModel

            ListModel {
                ListElement {
                    name: "韩伟"
                    from: "China"
                    dialog: "志不在高,在于不断创造和实现。"
                }
                ListElement {
                    name: "杰瑞杨"
                    from: "English"
                    dialog: "I will be successful!"
                }
                ListElement {
                    name: "金顺"
                    from: "Korea"
                    dialog: "有智者立长志,无志者长立志。"
                }
                ListElement {
                    name: "周炜溟"
                    from: "China"
                    dialog: "相信自己,一定能成功。"
                }
            }
        }

        Component {
            id: headerView

            Item {
                width: parent.width
                height: 32
                RowLayout {
                    anchors.left: parent.left
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 8

                    Text {
                        text: "Name"
                        font.bold: true
                        font.pixelSize: 20
                        Layout.preferredWidth: 120
                    }

                    Text {
                        text: "From"
                        font.bold: true
                        font.pixelSize: 20
                        Layout.preferredWidth: 80
                    }

                    Text {
                        text: "Dialog"
                        font.bold: true
                        font.pixelSize: 20
                        Layout.fillWidth: true
                    }
                }
            }
        }

        Component {
            id: footerView
            // 添加下面内容来调整显示内容
//            Text {
//                width: parent.width
//                font.italic: true
//                color: "blue"
//                height: 30
//                verticalAlignment: Text.AlignVCenter
//            }
            Item {
                id: footerRootItem
                width: parent.width
                height: 30
                property alias text: txt.text
                // 信号
                signal clean()
                signal add()
                signal clearone()
                //
                Text {
                    anchors.left: parent.left
                    anchors.top: parent.top
                    anchors.bottom: parent.bottom
                    id: txt
                    font.italic: true
                    color: "blue"
                    verticalAlignment: Text.AlignVCenter
                }
                Button{
                    id: clearAll
                    anchors.right: parent.right
                    anchors.verticalCenter: parent.verticalCenter
                    text: "Clear"
                    onClicked: footerRootItem.clean();
                }
                Button {
                    id: addOne

                    anchors.right: clearAll.left
                    anchors.rightMargin: 4
                    anchors.verticalCenter: parent.verticalCenter
                    text: "Add"
                    onClicked: footerRootItem.add()
                }
                Button {
                    id: clearone

                    anchors.right: addOne.left
                    anchors.rightMargin: 4
                    anchors.verticalCenter: parent.verticalCenter
                    text: "clearone"
                    onClicked: footerRootItem.clearone()
                }
            }
        }

        Component {
            id: dataDelegate

            Item {
                id: wrapper

                width: parent.width
                height: 30

                MouseArea {
                    anchors.fill: parent
                    onClicked: wrapper.ListView.view.currentIndex = index
                }

                RowLayout {
                    anchors.left: parent.left
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 8

                    Text {
                        id: coll
                        text: name
                        color: wrapper.ListView.isCurrentItem ? "red" : "black"
                        font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                        Layout.preferredWidth: 120
                    }

                    Text {
                        text: from
                        color: wrapper.ListView.isCurrentItem ? "red" : "black";
                        font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                        Layout.preferredWidth: 80
                    }

                    Text {
                        text: dialog
                        color: wrapper.ListView.isCurrentItem ? "red" : "black";
                        font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                        Layout.fillWidth: true
                    }
                }
            }
        }

        ListView {
            id: listView
            anchors.fill: parent
            delegate: dataDelegate

            model: phoneModel.createObject(listView)
            header: headerView
            footer: footerView
            focus: true
            highlight: Rectangle {
                color: "lightblue"
            }

            onCurrentIndexChanged: {
                if(listView.currentIndex >= 0) {
                    var data = listView.model.get(listView.currentIndex);
                    listView.footerItem.text = data.name + "," + data.from + "," + data.dialog;
                } else {
                    listView.footerItem.text = "-";
                }
            }
            function addOne(){
                model.append({
                                "name" : "韩文潞" ,
                                 "from" : "China",
                                 "dialog":"勇于挑战一切不可能!"
                             })
                // 加载临时添加的数据
            }
            // 写一个函数,清理当前的
            function delOne()
            {
                if(listView.currentIndex >= 0)
                {
                    var data = listView.model.get(listView.currentIndex);
                    console.log(data.name + "," + data.from + "," + data.dialog)
                    model.remove(listView.currentIndex)
                }
            }

            // 信号绑定
            Component.onCompleted: {
                listView.footerItem.clean.connect(listView.model.clear)
                listView.footerItem.clearone.connect(listView.delOne)
                listView.footerItem.add.connect(listView.addOne)
            }
        }
    }
}

运行效果:

image.png

2.4、 对ListView的C++新定义model数据进行增加和删除(待补充)