How does the QML ListView estimates the height/width of it's contentItem

荒凉一梦 提交于 2020-01-04 09:48:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!