我疯了。我在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。
答案 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
即可。