Reputation: 109
There is a well-known problem that has been discussed for a long time, for example, in this thread. However, there is no specific solution that I need, at least I have not found.
As we know, the ListView counts the contentHeight property based on the currently visible delegates. The number of these visible delegates depends on the value that is set for the cacheBuffer property. However, there can be many elements, and at the same time we cannot set the cacheBuffer
too large to create all of them. Therefore, we cannot know exactly the height of each delegate (since some of them are not displayed, which means that they are not created), which is why the contentHeight
is calculated as if on the assumption of the average height for each of them.
If all delegates have the same height then everything works well. Since the assumption about the height of the invisible elements comes true, because they are all the same and as a result, contentHeight
does not change (at least in theory).
However, if the delegates have different heights, then the contentHeight value will constantly change as the displayed delegates change. As a result, if there are any components that depend on contentheight
, they also change their behavior depending on the new value.
And thus we come to the most important thing: the components that depend on contentHeight. In my case it is a ScrollBar. Its height and position directly depend on the contentHeight
of the ListView
. Thus, every time the contentHeight
changes - this will provoke changes in the height and position of the ScrollBar
.
I would like to achieve this behavior so that no matter what elements the ListView
displays, contentHeight
should not change because of this and should always be equal to the sum of the heights of all delegates. As an example I can offer a list of messages in a Telegram. I need this behavior so that the scrollbar is always based on a specific height, and does not change its own height and position for no reason.
I understand that it is possible to increase the cacheBuffer
. However, it seems to me that this is a decision of the consequence and not of the reason. Therefore this option is unacceptable for me.
I understand how you can independently calculate the sum of all the heights of the delegates. I got the following code (used inside ListView
):
Component.onCompleted: {
var totalHeight = 0
for (var i = 0; i < listView.count; ++i) {
listView.currentIndex = i
totalHeight += listView.currentItem.height
}
}
As a result, totalHeight
is the value that, in my case, the contentHeight
property should be equal to. However, I can't just go and do the binding, like contentHeight: totalHeight
, or rather, it won't work, since contentHeight
will still be recalculated if visible delegates change, which will definitely happen when scrolling.
Based on this, it makes no sense to somehow try to manually set the size for the ScrollBar by changing the size property for it, because after a guaranteed change of the contentHeight
, the size
will also change (since it depends on it).
Is there a way to set some other algorithm for counting the contentHeight
of the ListView
?
Or at least make it so that the ScrollBar
does not depend on it, since it is constantly changing, but depends on the sum of the heights of the delegates?
If possible, is there a quick way to calculate the sum of the heights of all delegates, taking into account the fact that there can be many of them and their number can increase?
An full and extensive example of this problem can be seen in this created issue (you can also find more information about this problem).
In this thread, I will give only test code that can understand how I use ScrollBar
with ListView
, and how do I reach this problem.
main.qml
:
import QtQuick 2.11
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import "."
ApplicationWindow {
id: root
visible: true
width: 1280
height: 920
title: "ListViet test"
RowLayout {
id: content
anchors.fill: parent
PMListView {
id: listView
Layout.alignment: Qt.AlignCenter
width: 500
height: 900
model: 50
delegate: Rectangle {
id: holder
width: listView.width
height: 50 + (model.index % 3 == 0 ? randomInteger(500, 700) : 0)
border.width: 2
border.color: model.index % 3 ? "green" : "blue"
color: "grey"
Text {
anchors.centerIn: parent
text: model.index
font.pointSize: 22
color: "white"
}
}
}
}
function randomInteger(min, max) {
let rand = min + Math.random() * (max + 1 - min);
return Math.floor(rand);
}
}
PMListView.qml
:
import QtQuick 2.9
import QtQuick.Controls 2.2
ListView {
id: root
focus: true
clip: true
interactive: false
currentIndex: -1
ScrollBar.vertical: ScrollBar {
id: vScrollBar
policy: ScrollBar.AlwaysOn
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
propagateComposedEvents: true
onWheel: {
if (root.contentHeight > 0) {
root.contentY -= wheel.angleDelta.y
// force readjust on top and bottom positions (otherwise content may be shifted wrong way)
if (root.atYBeginning) {
root.positionViewAtBeginning()
}
if (root.atYEnd) {
root.positionViewAtEnd()
}
}
}
}
}
Upvotes: 1
Views: 1679
Reputation: 11
Listview
{
id: listview
...
...
}
ScrollBar {
id: vscrollbar
view: listview
visible: listview.height < listview.contentHeight
...
}
Here the visible property of scrollbar will decide the visibility based on the content height of the list view Thanks!
Upvotes: 0