首页 > 代码库 > Qt Quick之ListView下拉刷新数据

Qt Quick之ListView下拉刷新数据

    Qt Quick里的ListView,本身是Flickable的派生类,当你用鼠标拖曳或者手指触摸(触摸屏)时,会产生flickStarted和flickEnded两个信号,利用这两个信号,就可以实现下拉刷新数据,当然上拉刷新也是可以的。

    创建一个Qt Quick App项目,添加dynamicModel.h和dynamicModel.cpp两个文件,用于实现DynamicListModel。项目创建过程参考《Qt Quick 之 Hello World 图文详解》。

    我们实现的下拉刷新效果有点儿特别,每次刷新后,只保留预定义的一页数据,比如代码中默认的页大小为20。

    版权所有foruok,转载请注明出处:http://blog.csdn.net/foruok。

C++ model实现

    很简单,直接上代码了。

    dynamic.h:

#ifndef DYNAMICMODEL_H
#define DYNAMICMODEL_H
#include <QAbstractListModel>

class DynamicListModelPrivate;
class DynamicListModel : public QAbstractListModel
{
    Q_OBJECT
    Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize NOTIFY pageSizeChanged)
    Q_PROPERTY(int total READ total WRITE setTotal NOTIFY totalChanged)
public:
    DynamicListModel(QObject *parent = 0);
    ~DynamicListModel();

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

    Q_INVOKABLE void loadMore(bool forward);

    int pageSize();
    void setPageSize(int size);
    int total();
    void setTotal(int total);

signals:
    void pageSizeChanged(int size);
    void totalChanged(int total);

private:
    DynamicListModelPrivate *m_dptr;
};

#endif // DYNAMICMODEL_H

    dynamicModel.cpp:

#include "dynamicModel.h"
#include <QDebug>

class DynamicListModelPrivate
{
public:
    DynamicListModelPrivate(DynamicListModel *model)
        : m_model(model), m_start(0), m_end(20)
        , m_total(100), m_pageSize(20)
    {
        m_roleNames.insert(Qt::UserRole, "content");
    }

    void pageDown()
    {
        if(m_end < m_total)
        {
            m_start += m_pageSize;
            m_end += m_pageSize;
            if(m_end > m_total)
            {
                m_end = m_total;
                m_start = m_end - m_pageSize;
            }
        }
    }

    void pageUp()
    {
        if(m_start > 0)
        {
            m_start -= m_pageSize;
            if(m_start < 0) m_start = 0;
            m_end = m_start + m_pageSize;
        }
    }

    void adjustPageRange()
    {
        if(m_end - m_start < m_pageSize)
        {
            m_end = m_start + m_pageSize;
            if(m_end > m_total)
            {
                m_end = m_total;
                m_start = m_end - m_pageSize;
            }
        }
    }

    DynamicListModel *m_model;
    int m_start;
    int m_end;
    int m_total;
    int m_pageSize;
    QHash<int, QByteArray> m_roleNames;
};

DynamicListModel::DynamicListModel(QObject *parent)
    : QAbstractListModel(parent),
    m_dptr(new DynamicListModelPrivate(this))
{

}

DynamicListModel::~DynamicListModel()
{
    delete m_dptr;
}

int DynamicListModel::rowCount(const QModelIndex &parent) const
{
    return m_dptr->m_end - m_dptr->m_start;
}

QVariant DynamicListModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    //qDebug() << "index.row - " << row << " start - " << m_dptr->m_start;
    return QString::number(row + m_dptr->m_start);
}

QHash<int, QByteArray> DynamicListModel::roleNames() const
{
    return m_dptr->m_roleNames;
}

void DynamicListModel::loadMore(bool forward)
{
    beginResetModel();
    if(forward)m_dptr->pageDown();
    else m_dptr->pageUp();
    endResetModel();
}

int DynamicListModel::pageSize()
{
    return m_dptr->m_pageSize;
}

void DynamicListModel::setPageSize(int size)
{
    m_dptr->m_pageSize = size;
    m_dptr->adjustPageRange();
    emit pageSizeChanged(size);
}

int DynamicListModel::total()
{
   return m_dptr->m_total;
}

void DynamicListModel::setTotal(int total)
{
    m_dptr->m_total = total;
    m_dptr->adjustPageRange();
    emit totalChanged(total);
}

    DynamicListModel仅仅是演示用法,使用m_start、m_end、m_total、m_pageSize四个整型变量来模拟实际的数据。而data()方法,将ListView内的行序号加上m_start转换为字符串返回,就是我们在ListView界面上看到了文字了。

    loadMore()函数,区分向前还是向后加载数据,它调用DynamicListModel的pageDown()、pageUp()来更新内部的数据状态。在loadMore()一开始,调用beginResetModel(),通知关联到DynamicListModel上的view们刷新自己,当内部数据状态更新结束后,调用endResetModel()来通知view们,这样view们就会刷新,最终在实例化item delegate时调用data()方法来准备数据,此时m_start已变化,所以界面上看到的数字也跟着变了。

导出C++ Model

    这个简单,我们在《Qt Quick 之 QML 与 C++ 混合编程详解》一文中已经讲过。直接看main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "dynamicModel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    QQmlContext *ctx = engine.rootContext();
    ctx->setContextProperty("dynamicModel", new DynamicListModel());
    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

    return app.exec();
}

QML代码介绍

    是时候看看main.qml了:

import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.2

Window {
    width: 320;
    height: 480;
    minimumWidth: 300;
    minimumHeight: 480;
    visible: true;
    id: root;

    Component {
        id: listDelegate;
        Text {
            id: wrapper;
            width: parent.width;
            height: 32;
            font.pointSize: 15;
            verticalAlignment: Text.AlignVCenter;
            horizontalAlignment: Text.AlignHCenter;
            text: content;
            color: ListView.view.currentIndex == index ? "red" : "blue";
            MouseArea {
                anchors.fill: parent;
                onClicked: {
                    if(wrapper.ListView.view.currentIndex != index){
                        wrapper.ListView.view.currentIndex = index;
                    }
                }
            }
        }
    }

    ListView {
        id: dynamicList;
        z: 1;
        anchors.fill: parent;
        anchors.margins: 10;

        delegate: listDelegate;
        model: dynamicModel;

        focus: true;
        activeFocusOnTab: true;
        highlight: Rectangle {
            color: "steelblue";
        }

        property real contentYOnFlickStarted: 0;
        onFlickStarted: {
            //console.log("start,origY - ", originY, " contentY - ", contentY);
            contentYOnFlickStarted = contentY;
        }

        onFlickEnded: {
            //console.log("end,origY - ", originY, " contentY - ", contentY);
            dynamicModel.loadMore(contentY < contentYOnFlickStarted);
        }
    }
}

    定义ListView对象时,指定其model为main()函数中导出的dynamicModel,其它的代码不必细说了,咱们单看实现下拉(上拉)刷新的关键代码。

    onFlickStarted信号处理器,在这里我们仅仅是将flick开始时的contentY记录到contentYOnFlickStarted属性中。

    onFlickEnded信号处理器,这里比较flick结束时的contentY和开始时的contentY(即contentYOnFlickStarted),结束时小,说明是下拉,结束时大,说明是上拉。根据比较结果调用loadMore()。

    好啦,就这么简单了。看看效果。

下拉刷新效果

    图1是初始效果:


             图1动态刷新列表初始效果

    图2是下拉了两次后的效果:


             图2 下拉刷新后的效果

    图3是从图2所示状态上拉后的效果:


             图3 上拉后的效果


    版权所有foruok,转载请注明出处:http://blog.csdn.net/foruok。

    好啦,解说完毕。

    回顾本系列文章:

  • Qt Quick 简介
  • QML 语言基础
  • Qt Quick 之 Hello World 图文详解
  • Qt Quick 简单教程
  • Qt Quick 事件处理之信号与槽
  • Qt Quick事件处理之鼠标、键盘、定时器
  • Qt Quick 事件处理之捏拉缩放与旋转
  • Qt Quick 组件与对象动态创建详解
  • Qt Quick 布局介绍
  • Qt Quick 之 QML 与 C++ 混合编程详解
  • Qt Quick 图像处理实例之美图秀秀(附源码下载)
  • Qt Quick 之 PathView 详解
  • Qt Quick实例之挖头像
  • Qt Quick综合实例之文件查看器

Qt Quick之ListView下拉刷新数据