How to know when ListView item enters the visible area?
-
Hi,
I'm looking for a way to know when an item in my ListViews enters the visible area, so that after a certain delay I can mark that item as read. I can't find any signal, and knowing that ListView preloads some items when they are about to be shown makes things even harder. I think this is a common problem, because in nearly all social apps you need to know when a notification/message is read by the user...
Thanks in advance!
-
I use different techniques:
If the ListView shows only one item in the screen than I set highlightRangeMode to ListView.StrictlyEnforceRange and in this way the currentItem is always set to the only item on the screen and via onCurrentIndexChanged it's possible to track which item is on the screen
If the ListView shows more than one item in the screen than I use contentX (or contentY) for calculate which items are currently displayed on the screen. You don't have a signal that tell you which items are visible, so you have to calculate in some ways. Probably there is some alternative, but with ListView I found easy to use contentX (or contentY).
Another alternative, (but, I never used), If the ListView shows more that one item but you just need to track one a time, you can set preferredHighlightBegin and preferredHighlightEnd to a range where typically you can suppose that the user has more attention on the item and setting hightlightRangeMode to ListView.ApplyRange then the currentIndex always follow the scrolling and you can use onCurrentIndexChanged
-
I agree with Cianlucas suggestions. ListView will not actually preload items by default unless you ask for it and you can explicitly set the cacheBuffer to 0 to prevent that. So for most cases you can still rely on the create/destroy signals for your delegates though an item might be just outside of the visible area. In my own app I collapsed messages by default, forcing the user to expand message content as in an e-mail client, but that would be too inconvenient for most messaging apps.
Here is a simple example that shows how you can track randomly sied items beeing completely visible in view:
@
import QtQuick 2.3
import QtQuick.Controls 1.2ApplicationWindow {
visible: true
width: 640
height: 480ListView { id: list anchors.fill: parent model: 1000 cacheBuffer:1000 delegate: Rectangle { id: dg property int yoff: Math.round(dg.y - list.contentY) property bool isFullyVisible: (yoff > list.y && yoff + height < list.y + list.height) border.color: Qt.darker(color, 1.2) color: isFullyVisible ? "#ccffcc" : "#fff" height: Math.random() * 200 width: parent.width Text {text: "Fully visible:" + isFullyVisible ; anchors.centerIn: parent} } }
}
@
-
Thank you guys, it works wonders!
Jens, your code is perfect, I just had to realize that list.y in my case is relative to its parent while the item's y is relative to the list, so in your case list.y and the first element's y are both 0 and there are no problems; in my code I had list.y = 44 and the delegate's y was 0, so I just removed list.y from the equation! And added equality to the comparison operators to include the first (and last) items :DStill, I wonder why such a simple signal isn't emitted... I mean, from what I understand every visual object goes through a painting() event when it becomes visible, so why not just bubble that signal up to QML?
Thanks again!
-
Etchelon: It's not as simple as you might think. When using hardware accelleration as we do in Qt 5, you don't always know if a rendered Item is actually visible to the user without some overhead. That said, ListView in particular can easily find out if an item is visible as evidenced by the example above. Given the fairly easy workaround, I am not sure if it is worth adding extra signals and performance overhead in order to make the API more convenient but it's certainly possible.