Qml Listview项目在滚动时消失

Posted

技术标签:

【中文标题】Qml Listview项目在滚动时消失【英文标题】:Qml Listview items disappear when scrolling 【发布时间】:2017-02-07 21:08:38 【问题描述】:

我有以下带有列表视图的滚动视图:

ScrollView
    anchors.fill: parent
    ListView
        id: lvCommitsBranch
        model: git.getCommitsBranch();
        clip: true
        delegate: Rectangle 
            height: 100
            width: parent.width
            Text 
                anchors.left: parent.left
                font.bold: true
                text:model.author
                id:txtName
            
            Text
                anchors.left: parent.left
                anchors.top:txtName.bottom
                font.pixelSize: 10
                text:model.email
                id: txtEmail
            
            Text 
                anchors.left: parent.left
                anchors.top:txtEmail.bottom
                text: model.message + ' ' + model.hash
                id: txtMsg
            
            MouseArea
                anchors.fill: parent
                onClicked: 
                    lvCommitsBranch.currentIndex = index;
                    console.log('Msg: ' + model.message);
                    console.log('Hash: ' + model.hash);
                
                acceptedButtons: Qt.LeftButton | Qt.RightButton
            
        
    

问题是,当我滚动时,一些项目会消失(每次随机滚动,有时我必须快速滚动,但并非总是如此)。

当我点击没有消失的项目时,我会在所有模型的属性上得到undefined。当 Mousearea 的 onclick 被触发时,它会打印以下内容:

qml: 消息:未定义

qml: 哈希:未定义

我从我的git 自定义组件返回的方法 (QAbstractListModel) 中获取模型信息。

这是我的 QAbstractListModel:

标题:

class CommitsBranch : public QAbstractListModel

    Q_OBJECT
public:
    enum Roles 
        AuthorRole,
        EMailRole,
        MsgRole,
        DateRole,
        HashRole
    ;
    explicit CommitsBranch(QObject *parent = 0);
    CommitsBranch(Repository *repo);
public:
    virtual int rowCount(const QModelIndex &parent) const override;
    virtual QVariant data(const QModelIndex &index, int role) const override;
protected:
    // return the roles mapping to be used by QML
    virtual QHash<int, QByteArray> roleNames() const override;
private:
    QList<Commit> m_data;
    QHash<int, QByteArray> m_roleNames;

;

Cpp:

CommitsBranch::CommitsBranch(QObject *parent)
    : QAbstractListModel(parent)



CommitsBranch::CommitsBranch(Repository *repo)

    m_roleNames[AuthorRole] = "author";
    m_roleNames[EMailRole] = "email";
    m_roleNames[MsgRole] = "message";
    m_roleNames[DateRole] = "date";
    m_roleNames[HashRole] = "hash";

    /*
    here we append the m_data (QList) Items using libgit2 methods
    */



int CommitsBranch::rowCount(const QModelIndex &parent) const

    Q_UNUSED(parent);
    return m_data.count();


QVariant CommitsBranch::data(const QModelIndex &index, int role) const
    
    // this function returns the required data


QHash<int, QByteArray> CommitsBranch::roleNames() const

    return m_roleNames;

而git只是一个继承自QObject的类,它有如下方法:

Q_INVOKABLE QObject* getCommitsBranch();
QObject *Git::getCommitsBranch()

    CommitsBranch* files = new CommitsBranch(repo.data());
    return files;

没有滚动视图我会得到相同的行为。

编辑: 如果我使用一个包含大量提交的存储库(更多行到列表视图),即使增加 cacheBuffer 也无济于事,如果我滚动得快一点,所有项目都会消失。

【问题讨论】:

问题可能源于滚动时视图自动创建和销毁委托。一个肮脏的快速解决方案是增加视图的cacheBuffer - 这是要预加载的像素数量。 QML 有时被称为losing track of its sheep,可以这么说,检查滚动是否不会意外删除实际的模型项。 我尝试将cacheBuffer 增加到100,但问题仍然存在。我再次将其更改为 1000,现在项目不会消失,但有时我仍然在项目的属性中未定义(还有一些奇怪的视觉错误,如文本移动 o.o) 尝试在您的委托中将Item 包裹在Rectangle 周围。喜欢delegate: Item Rectangle ... @DuKes0mE 结果完全一样,没有任何变化:s 嗯,它应该避免你的矩形被删除。 【参考方案1】:

这里的问题是,默认情况下,如果您返回一个 QObject*,它会将所有权转移给 QML。

http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership

这个规则的例外是当一个 QObject 从一个 显式 C++ 方法调用:在这种情况下,QML 引擎假定 对象的所有权,除非该对象的所有权具有 通过调用显式设置为保留在 C++ 中 QQmlEngine::setObjectOwnership() 与 QQmlEngine::CppOwnership 指定。

您必须手动设置返回的 QObject* 所有权,因此它不会被 QML 引擎破坏:

QObject *Git::getCommitsBranch()

    CommitsBranch* files = new CommitsBranch(repo.data());

    QQmlEngine::setObjectOwnership(files, QQmlEngine::CppOwnership)

    return files;

请注意,您的CommitsBranch 对象将永远不会被删除,因此您将发生内存泄漏。但至少你的 QML 项目不应该再消失了!

编辑:按照建议,您可以这样做以避免内存泄漏:

// CommitsBranch Constructor
CommitsBranch::CommitsBranch(Repository *repo, QObject *parent) :
    QAbstractListModel(parent)  /*stuff*/ 

QObject *Git::getCommitsBranch()

    // Setting ownership is not necessary if you pass the parent to the QAbstractListModel
    CommitsBranch* commits = new CommitsBranch(repo.data(), this);

    return files;

【讨论】:

你没看错,但是 CommitsBranch 似乎是列表模型,我猜在滚动列表时不能被 QML 删除!? 当您滚动列表时,模型不会被 QML 破坏,但是当 QML 假定它不再需要模型时。它随时可能发生。如果列表不动,QML 就不需要访问模型,因此您无法判断模型是否在这里。然而,滚动会强制 QML 读取模型,并且您会注意到某些内容已关闭。 我会尽快测试。为了避免内存泄漏,我应该使用 QSharedPointer 还是不是一个好主意? 有效!非常感谢!也可以这样工作:CommitsBranch* commits = new CommitsBranch(repo.data(), (QObject*)this); 并将构造函数更改为CommitsBranch(Repository *repo, QObject *parent);CommitsBranch::CommitsBranch(Repository *repo, QObject *parent): QAbstractListModel(parent)/*stuff*/。如果您不介意,请将其添加到您的答案中作为替代方案。 共享指针不适用于 QML,它使用自己的共享对象类,这些类不属于公共 api。这很烦人,因为 QML 内存管理在几个用例中损坏,您必须像 20 年前那样进行手动内存管理......有一个关于它的错误报告,但没有工作尽管它被认为很关键,但已经在 2 年多的时间里完成了它:bugreports.qt.io/browse/QTBUG-50319

以上是关于Qml Listview项目在滚动时消失的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义滚动条的 QML Listview

QML ListView Loader 不需要的预定义行为

Android ListView 不会在滚动时重绘

在 ListView 中滚动时 CheckedTextView 消失

停止 ListView 滚动动画

Windows 10 ScrollIntoView() 没有滚动到列表视图中间的项目