问题
I am wondering how the ListView
estimates the height/width of it's contentItem
, though the delegate is a Component
that you can't ask, and the size might vary from one delegate-instance to another.
- It does not use the average size of the current instances.
Otherwise in Example 1, if one element is pressed, the estimated size would be 3055.5 if all instances are counted, or 3125 if only those in the view are counted. It estimates 2845 - It does not use the maximums size of all current instances.
If one element is pressed, the estimated size would be 7500. If the maximum height is only considered for non-instantiated elements, it would be 6350 - It does not use the minimum size of all current instances.
The estimation would be definitely to small.
And even if you know the right size, you can't help it, for in Example 1 we know the correct contentHeight
would be 2500 if nothing is pressed, and 2650 if something is pressed. In Example 2 the right contentHeight
would be 1225 but setting manually setting the value is of no use, as it is overwritten soon after.
Example 1:
ListView {
id: lv1
width: 200
height: 500
model: 50
onContentHeightChanged: console.log('estimated contentHeight', contentHeight)
delegate: Rectangle {
width: 200
height: ma.pressed ? 150 : 50
border.color: 'black'
Text {
anchors.centerIn: parent
text: index
}
MouseArea {
id: ma
anchors.fill: parent
}
}
}
Example 2:
ListView {
id: lv1
width: 200
height: 500
model: 50
onContentHeightChanged: console.log('estimated contentHeight', contentHeight)
delegate: Rectangle {
width: 200
height: index
border.color: 'black'
Text {
anchors.centerIn: parent
text: index + ' ' + ((index * (index + 1)) / 2)
}
}
}
回答1:
I don't know. Let's look through the source code. :)
Looking at the code, here's where the contentWidth
and contentHeight
are set:
void QQuickItemViewPrivate::updateViewport()
{
Q_Q(QQuickItemView);
qreal extra = headerSize() + footerSize();
qreal contentSize = isValid() || !visibleItems.isEmpty() ? (endPosition() - startPosition()) : 0.0;
if (layoutOrientation() == Qt::Vertical)
q->setContentHeight(contentSize + extra);
else
q->setContentWidth(contentSize + extra);
}
The startPosition()
and endPosition()
functions are declared here:
qreal QQuickItemViewPrivate::startPosition() const
{
return isContentFlowReversed() ? -lastPosition() : originPosition();
}
qreal QQuickItemViewPrivate::endPosition() const
{
return isContentFlowReversed() ? -originPosition() : lastPosition();
}
QQuickListView
's implementations of originPosition()
and lastPosition()
are both here:
qreal QQuickListViewPrivate::originPosition() const
{
qreal pos = 0;
if (!visibleItems.isEmpty()) {
pos = (*visibleItems.constBegin())->position();
if (visibleIndex > 0)
pos -= visibleIndex * (averageSize + spacing);
}
return pos;
}
qreal QQuickListViewPrivate::lastPosition() const
{
qreal pos = 0;
if (!visibleItems.isEmpty()) {
int invisibleCount = INT_MIN;
int delayRemovedCount = 0;
for (int i = visibleItems.count()-1; i >= 0; --i) {
if (visibleItems.at(i)->index != -1) {
// Find the invisible count after the last visible item with known index
invisibleCount = model->count() - (visibleItems.at(i)->index + 1 + delayRemovedCount);
break;
} else if (visibleItems.at(i)->attached->delayRemove()) {
++delayRemovedCount;
}
}
if (invisibleCount == INT_MIN) {
// All visible items are in delayRemove state
invisibleCount = model->count();
}
pos = (*(--visibleItems.constEnd()))->endPosition();
if (invisibleCount > 0)
pos += invisibleCount * (averageSize + spacing);
} else if (model && model->count()) {
pos = (model->count() * averageSize + (model->count()-1) * spacing);
}
return pos;
}
averageSize
seems to be calculated here:
void QQuickListViewPrivate::updateAverage()
{
if (!visibleItems.count())
return;
qreal sum = 0.0;
for (int i = 0; i < visibleItems.count(); ++i)
sum += visibleItems.at(i)->size();
averageSize = qRound(sum / visibleItems.count());
}
That function is called by QQuickListViewPrivate::visibleItemsChanged(), which is called by QQuickItemViewPrivate::refill(), which is called... all over the place. Basically whenever anything related to the view or its items changes.
visibleItems.at(i)->size()
is pretty much equivalent to QQuickItem::width()
/QQuickItem::height()
:
inline qreal itemWidth() const { return item ? item->width() : 0; }
inline qreal itemHeight() const { return item ? item->height() : 0; }
From what I can see, it more or less takes the height of every item that is visible (could probably be equated with "currently instantiated") in the view, adds them together and then divides that sum by the number of visible items. You can confirm this by adding the following debug output:
$ git diff
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 0351077..acfb88a 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -1296,6 +1296,7 @@ void QQuickListViewPrivate::updateAverage()
for (FxViewItem *item : qAsConst(visibleItems))
sum += item->size();
averageSize = qRound(sum / visibleItems.count());
+ qDebug() << "sum" << sum << "visibleItems.count()" << visibleItems.count();
}
qreal QQuickListViewPrivate::headerSize() const
The output of this before clicking an item:
qml: estimated contentHeight 5000
sum 550 visibleItems.count() 11
qml: estimated contentHeight 2500
sum 850 visibleItems.count() 17
After pressing on the first item:
sum 850 visibleItems.count() 15
qml: estimated contentHeight 2845
So I would say that
It does not use the average size of the current instances.
is incorrect to some degree. If you really want to know what the difference is, you'll need to look further into the code.
来源:https://stackoverflow.com/questions/42780845/how-does-the-qml-listview-estimates-the-height-width-of-its-contentitem