QML 列表视图如何估计其内容项的高度/宽度



我想知道ListView如何估计它contentItem的高度/宽度,尽管委托是一个你不能问的Component,并且大小可能因一个委托实例而异。

  • 它不使用当前实例的平均大小。
    否则,在示例 1中,如果按下一个元素,则估计大小为3055.5(如果计算所有实例)或3125(如果仅计算视图中的实例)。它估计2845
  • 它不使用所有当前实例的最大大小。
    如果按下一个元素,估计大小将为7500。如果仅考虑非实例化元素的最大高度,则为6350
  • 它不使用所有当前实例的最小大小。
    估计肯定很小。

即使您知道正确的尺寸,您也忍不住,因为在示例 1中,我们知道如果什么都没有按下,正确的contentHeight将是2500,如果按下某些东西,正确的将是2650。在示例 2中,正确的contentHeight1225,但手动设置该值是没有用的,因为它很快就会被覆盖。

示例 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
}
}
}

示例 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)
}
}
}

我不知道。让我们看一下源代码。:)

查看代码,下面是设置contentWidthcontentHeight的位置:

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);
}

startPosition()endPosition()函数声明如下:

qreal QQuickItemViewPrivate::startPosition() const
{
return isContentFlowReversed() ? -lastPosition() : originPosition();
}
qreal QQuickItemViewPrivate::endPosition() const
{
return isContentFlowReversed() ? -originPosition() : lastPosition();
}

QQuickListVieworiginPosition()lastPosition()的实现都在这里:

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似乎在这里计算:

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());
}

该函数由QQuickListViewPrivate::visibleItemsChanged()调用,由QQuickItemViewPrivate::refill()调用,称为...到处都是。基本上,每当与视图或其项目相关的任何内容发生更改时。

visibleItems.at(i)->size()几乎等同于QQuickItem::width()/QQuickItem::height()

inline qreal itemWidth() const { return item ? item->width() : 0; }
inline qreal itemHeight() const { return item ? item->height() : 0; }

从我所看到的,它或多或少地取视图中每个可见项目(可能等同于"当前实例化")的高度,将它们相加,然后将总和除以可见项目的数量。您可以通过添加以下调试输出来确认这一点:

$ 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

单击项目之前的输出:

qml: estimated contentHeight 5000
sum 550 visibleItems.count() 11
qml: estimated contentHeight 2500
sum 850 visibleItems.count() 17

按下第一项后:

sum 850 visibleItems.count() 15
qml: estimated contentHeight 2845

所以我会说

它不使用当前实例的平均大小。

在某种程度上是不正确的。如果你真的想知道有什么区别,你需要进一步研究代码。

最新更新