0
votes

I have a WML application with a ScrollView and a ListView inside. The items from the ListView has variable height.

I need to know when the scroll bar is moved, in fact when it is at bottom and when it is NOT. My target is to keep scroll at bottom (positionViewAtEnd()) when I add an item to the ListView ONLY if the scroll is at bottom. If NOT at bottom, positionViewAtEnd() will not be used.

I have tried "playing" with height, contentHeigh & contentY. Sometimes it works (when scroll is at bottom: contentHeight == contentY + height), but other times contentY value changes to negative values, and my code fails...

Any help?

Thanks a lot

Diego


Thanks J-P Nurmi,

I have tried with 'atYEnd' in several property changes, to detect if scroll is at bottom or not

It seems to work, and then I use that in 'onCountChanged' to put (or not) the scroll at the bottom

That works once all the ListView height is full of messages, but NOT in ONE case: when the incoming message is the one that fills the ListView height (1st time that contentY is NOT '0').

I don't know if it is clear...

I have simplified my code to test (including the delegate), and now it seems like that:

 FocusScope {
        clip: true

        id: focusScopeView

        width: parent.width; height: parent.height

        ScrollView {

            width: parent.width; height: parent.height

            ListView {
                id: listTexts

                width: parent.width; height: parent.height

                property bool bScrolled: false

                model: textsModel
                delegate: Text { text: "Contact:\t" + eventText }

                onCountChanged: {

                    if (!bScrolled)
                         positionViewAtEnd();
        }

                onContentYChanged: {
                    bScrolled = !atYEnd;

                    if (atYEnd)
                        positionViewAtEnd()
        }

                onContentHeightChanged: {
                    if (!bScrolled)
                         positionViewAtEnd();
            }
            }
        }
   }

Thanks and regards!

Diego

3

3 Answers

1
votes

The reason why the calculation fails is that ListView has had to adjust its origin due to delegates of variable height. Including originY to the calculation would help to get the correct value, but there is a much more convenient way to check if any Flickable is atYEnd.

EDIT: This is a bit of a trick, but another way to do the auto-scrolling (or rather, to avoid the need of scrolling) is to use ListView.BottomToTop as a vertical layout direction. Then, instead if appending to the end, insert new messages at the beginning of the model. This way the content stays aligned to the bottom when new messages arrive, without doing any manual positioning. There's a little catch, though. The content is also aligned to the bottom when there is less content than fits in the view.

1
votes
  ListView {
        onContentYChanged: {
             if (contentY === contentHeight - height) {
               console.log("scrolled to bottom");
             }
        }
    }


    ScrollView {
            flickableItem.onContentYChanged: {
                if (flickableItem.contentY === flickableItem.contentHeight - viewport.height) {
                    console.log("scrolled to bottom");
                }
            }
        }
0
votes
ListView { 
        ScrollBar.vertical: ScrollBar {
                id: taskScroll
            }
        property bool atBottom: (taskScroll.position + taskScroll.size) == 1
    }