16

我要疯了。我在 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()(在文件末尾)实际上将其跳转到开头。如果没有调用,视图将保持在原来的位置,即使新条目出现在列表中也是如此。事实上,如果你查看ListView.positionViewAtEnd() 的 Qt 文档,它会说:

将视图定位在开头或结尾,考虑到...

这是文档中的愚蠢错误,还是什么?我已经尝试了我能想到的一切来完成这项工作,特别是positionViewAtIndex()使用荧光笔强制滚动发生的方法和使用荧光笔。但没有任何效果。注意/* This works. */上面源代码中的注释。启用后,它工作得很好!(当然,除了它跳转到 ListView.count()-2 索引,而不是列表的末尾)

有谁知道这里可能出了什么问题?我可以尝试证明 QML 中有一个可怕的、可怕的错误的任何例子吗?

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

谢谢!


编辑 1

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

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


编辑 2

这怎么行?

    ListView {
        id: messageList
        model: textMessageFiltered
        delegate: messageDelegate

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

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

我完全被难住了。感觉是个bug。

4

2 回答 2

8

您还需要设置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++)})
        }
    }
}

仍然有一些跳转到第一个元素并跳回几分之一秒。

于 2014-09-25T21:46:57.133 回答
6

文档中有一条注释:

注意:方法只能在组件完成后调用。要在启动时定位视图,应由 Component.onCompleted 调用此方法。

改变你ListView.onAdd:

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

而且效果很好。

在您的情况下, ListViewadd在创建和完成新委托之前发出信号。ListView 仍在幕后工作,因此positionViewAtEnd无法按预期工作。并且/* This works. */因为它是在新委托完成后调用的。但是,不要认为这总是有效的。只需按照文档positionViewAtEnd中的说明,调用。Component.onCompleted

于 2014-09-13T09:10:34.787 回答