1. ListView简单介绍
** ListView是一个用作列表条目显示的控件工具,在实际开发当中使用的很多,管理数据库列表和显示等等,但是Qt QML自带的ListView显示的数据相对比较简单,不能很好应该与实际开发中复杂的数据结构的显示,其中条目对应的数据主要是来自于Model,应该需要进行管理和设计Model进行实际中的开发运用,而外观显示则是有Delegate进行决定的,这也是QT中Model-View编程框架的使用规则。**
在QML中,model和view由delegate连接,职责划分如下:
- Model提供数据,每个数据项,可能有多个值,例如,每个电话薄条目都有姓名、图片和号码。
- 数据排列在一个view中,其中每个项都使用delegate进行可视化。
- View的任务是排列delegate,而每个delegate进行可视化。
- Delegate知道model的内容以及如何可视化它。
- View知道delegate的概念以及如何布置他们。
- 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"
}
}
}
运行效果:
2.2、 结合C++定义QLsitModel「QAbstractListModel」(实现ListView的使用):
创建MyListModel:
#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)
}
}
}
}
运行效果: