Метод QML ListView positionViewAtEnd() делает прямо противоположное
Я схожу с ума. У меня есть ListView внутри ScrollView, подключенный к модели, которая наследует 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 для ListView.positionViewAtEnd (), она говорит:
Позиционирует вид в начале или в конце, учитывая...
Это глупая ошибка в документации или как? Я перепробовал все, что мог придумать, чтобы сделать эту работу, особенно positionViewAtIndex()
метод и использование маркеров, чтобы вызвать прокрутку. Но ничего не работает. Обратите внимание /* This works. */
комментарий в исходном коде выше. Когда это включено, это работает совершенно нормально! (кроме, конечно, он переходит к индексу ListView.count()-2, а не к концу списка)
Кто-нибудь знает, что здесь может быть не так? Какие-нибудь примеры, которые я мог бы попытаться доказать, что есть ужасная, ужасная ошибка в QML?
Я использую Qt 5.3.1 с QtQuick 2.0 (или 2.1 или 2.2 тоже не удается). Я перепробовал много, много других конфигураций и кода, поэтому, пожалуйста, спросите, нужна ли вам дополнительная информация. Я полностью исчерпал свое Google-фу.
Спасибо!
Редактировать 1
Хотя принятый ответ решает вышеуказанную проблему, он включает в себя добавление Component.onCompleted
делегату. Кажется, это вызывает проблемы при прокрутке списка, потому что (я полагаю) делегаты добавляются в представление при прокрутке вверх, вызывая вызов триггера onCompleted, даже если элемент модели не является новым. Это крайне нежелательно. На самом деле приложение зависает, когда я пытаюсь прокрутить вверх, а затем добавить новые элементы в список.
Похоже, мне нужен сигнал model.onAdd() вместо использования экземпляра делегата для запуска прокрутки. Есть идеи?
Редактировать 2
И как это НЕ работает?
ListView {
id: messageList
model: textMessageFiltered
delegate: messageDelegate
onCountChanged: {
console.log("This prints properly.")
messageList.positionViewAtEnd()
}
}
Печатается текст "Это печатает правильно", так почему же он не позиционируется? На самом деле, кажется, чтобы сбросить позицию наверх. Так я попробовал positionViewAtBeginning()
, но это сделало то же самое.
Я полностью в тупике. Это похоже на ошибку.
2 ответа
Вам нужно установить 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++)})
}
}
}
Все еще есть некоторые прыжки к первому элементу и прыжки вниз на долю секунды.
В документации есть примечание:
Примечание: методы должны вызываться только после завершения Компонента. Чтобы позиционировать представление при запуске, этот метод должен вызываться Component.onCompleted.
Измени свой ListView.onAdd:
в
Component.onCompleted: {
console.log("This prints just fine!")
messageList.positionViewAtEnd()
}
И это хорошо работает.
В вашем случае ListView испускает add
сигнал до того, как новый делегат будет создан и завершен. ListView все еще работает над чем-то за сценой, поэтому positionViewAtEnd
не может работать как ожидалось. А также /* This works. */
потому что он вызывается после завершения нового делегата. Однако не думайте, что это всегда работает. Просто следуйте примечанию, позвоните positionViewAtEnd
в Component.onCompleted
, в документации.