QML ListView方法positionViewAtEnd()完全相反

时间:2014-09-12 19:22:18

标签: qt listview qml qt5.3

我疯了。我在ScrollView中有一个ListView,连接到一个继承QAbstractListModel的模型。将对象添加到模型时,ListView使用委托显示它们。到目前为止,非常好。

但我真的希望视图保持滚动到底部(就像聊天窗口一样),而且我很难实现这一点。以下是相关的QML代码:

Rectangle {
    ScrollView {
        [anchor stuff]

        ListView {
            id: messageList
            model: textMessageFiltered
            delegate: messageDelegate
        }
    }

    TextField {
        id: messageEditor
        [anchor stuff]

        onAccepted: {
            controller.sendTextMessage(text)
            text = ""

            /* This works. */
            //messageList.positionViewAtEnd();
        }
    }

    Component {
        id: messageDelegate
        Rectangle {
            anchors.left: parent.left
            anchors.right: parent.right

            color: "white"
            height: nameText.height + 4

            Text {
                id: nameText
                wrapMode: Text.Wrap
                text: "<b>" + authorName + " (" + authorId + ")</b>  " + message
                [anchor stuff]
            }
            ListView.onAdd: {
                console.log("This prints just fine!")
                messageList.positionViewAtEnd()
            }
        }
    }
}

真奇怪的是,messageList.positionViewAtEnd()(在文件的末尾)实际上将它跳到了开头。如果没有调用,视图将保持原样,即使列表中出现新条目也是如此。事实上,如果你看一下Qt documentation for the ListView.positionViewAtEnd(),它会说:

  

将视图置于 开头或结尾 ,并考虑到...

这是文档中的愚蠢错误,还是什么?我已经尝试了所有我能想到的工作,尤其是positionViewAtIndex()方法,并使用荧光笔强制滚动发生。但没有任何作用。请注意上面源代码中的/* This works. */评论。当启用它时,它完全正常! (当然,它跳转到ListView.count() - 2索引,而不是列表的末尾)

有没有人知道这里可能有什么问题?我可以尝试证明QML中存在一个可怕的,可怕的错误的任何例子吗?

我正在使用Qt 5.3.1与QtQuick 2.0(或2.1或2.2也失败)。我已经尝试了很多很多其他配置和代码,所以请询问您是否需要更多信息。我已经完全耗尽了我的谷歌。

谢谢!


修改1

虽然接受的答案确实解决了上述问题,但它涉及将Component.onCompleted添加到委托。当您滚动列表时,这似乎会导致问题,因为(我相信)向上滚动时会将代理添加到视图中,从而导致调用onCompleted触发器,即使模型项不是新项也是如此。这是非常不受欢迎的。实际上,当我尝试向上滚动然后将新元素添加到列表时,应用程序会冻结。

似乎我需要一个model.onAdd()信号,而不是使用委托实例的存在来触发滚动。有什么想法吗?


修改2

这怎么不起作用?

    ListView {
        id: messageList
        model: textMessageFiltered
        delegate: messageDelegate

        onCountChanged: {
            console.log("This prints properly.")
            messageList.positionViewAtEnd()
        }
    }

文本“打印正确”打印,为什么它不会定位?实际上,它似乎将位置重置为顶部。所以我尝试了positionViewAtBeginning(),但这也做了同样的事情。

我完全难过了。感觉就像一个bug。

2 个答案:

答案 0 :(得分:6)

您还需要设置currentIndex

testme.qml

import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Window 2.0

ApplicationWindow {
    title: qsTr("Hello World")
    width: 300
    height: 240

    ScrollView {
        anchors.fill: parent

        ListView {
            anchors.fill: parent

            id: messageList
            model: messageModel
            delegate: Text { text: mytextrole }
            highlight: Rectangle { color: "red" }
            highlightMoveDuration: 0

            onCountChanged: {
                var newIndex = count - 1 // last index
                positionViewAtEnd()
                currentIndex = newIndex
            }
        }
    }

    ListModel {
        id: messageModel
        ListElement { mytextrole: "Dog"; }
        ListElement { mytextrole: "Cat"; }
    }

    Timer {
        property int counter: 0
        running: true
        interval: 500
        repeat: true

        onTriggered: {
            messageModel.append({"mytextrole": "Line" + (counter++)})
        }
    }
}

还有一些跳到第一个元素并跳回来几分之一秒。

答案 1 :(得分:5)

documentation中有一条说明:

  

注意:只应在Component完成后调用方法。要在启动时定位视图,应该通过Component.onCompleted调用此方法。

ListView.onAdd:更改为

Component.onCompleted: {
    console.log("This prints just fine!")
    messageList.positionViewAtEnd()
}

效果很好。

在您的情况下,ListView在创建和完成新委托之前发出add信号。 ListView仍在处理场景背后的某些事情,因此positionViewAtEnd无法按预期工作。而/* This works. */因为它是在新委托完成后调用的。但是,不要认为这总是有效。只需按照说明操作,在文档中调用positionViewAtEnd中的Component.onCompleted即可。