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