Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

ListView kinetic scrolling speed



  • Hello,

    after I spent many hours with investigating my problem, I need to ask for a help. I have no idea what else could I try to identify and fix it.

    Let's describe what's happening. I wrote a music player with a library and a playlist. If I run it on a desktop, everything works as expected. But on Android the kinetic scrolling doesn't work correctly. If I hold finger on the touchscreen and move the playlist up and down, it moves very smooth and fast. Just like expected. But as soon as I release my finger, the scrolling slows down rapidly. So I can not scroll the ListView fast, which is really needed with long playlists.

    I thought that maybe my delegate is to complicated, so I simplified it only to a rectangle and a text. But it didn't help. I tried to use a different model with only numbers instead of my C++ QAbstractListModel. But that didn't help neither.

    Exactly the same problem is also in the library browser which uses different delegate and model. So it must be somewhere else in the application. But where could it be?

    The complete code can be found here. Do you have any idea what could be the problem and what could I do to identify it?

    This is the playlist item delegate although I don't think the problem is here:

    import QtQuick 2.6
    
    FocusScope {
        id: playlistItem
        property string pl_title
        property string pl_artist
        property string pl_album
        property string pl_queue
        property int itemHeight: config.libraryFontSize*2
        property bool selected: false
        anchors.right: parent ? parent.right : playlistDelegate.right
        anchors.left: parent ? parent.left : playlistDelegate.left
        height: itemHeight
    
        Keys.onReturnPressed: {
            controller.playTrack(index);
        }
    
        Keys.onSpacePressed: {
            if (config.playbackState == "playing")
                controller.buttonPressed("pause");
            else if (config.playbackState == "paused")
                controller.buttonPressed("play");
            else
                controller.playTrack(index);
        }
    
        Timer {
            id: resizeTimer
            interval: 5000
            repeat: false
            onTriggered: {
                index == playlistView.currentIndex ? playlistItem.state = 'Current' :  playlistItem.state = 'State0';
                playlistItem.selected = false;
                playlist.contentYOffset -= config.libraryFontSize*2;
            }
        }
    
        Timer {
            id: scrollTimer
            interval: 200
            repeat: false
            property int selectIndex
            onTriggered: {
                playlistView.highlightFollowsCurrentItem = true;
                playlistView.currentIndex = selectIndex;
            }
        }
    
        Rectangle {
            id: frame
            anchors.fill: parent
            anchors.topMargin: units.dp(2)
            anchors.bottomMargin: units.dp(2)
            color: "#00000000"
            radius: itemHeight/4
            border.width: units.dp(2)
            border.color: main.borderColor
            
            MouseArea {
                id: itemMouseArea
                anchors.fill: parent
                anchors.rightMargin: 0
    
                onClicked: {
                    if (!playlist.itemMoving) {
                        playlistTab.focus = true;
                        if (playlistItem.state == 'Selected') {
                            resizeTimer.stop();
                            playlistView.currentIndex = index;
                            playlistItem.state = 'Current';
                            playlist.contentYOffset -= config.libraryFontSize*2;
                            playlistItem.selected = false;
                        }
                        else {
                            playlistView.currentIndex = -1;
                            var center = playlistView.indexAt(10, playlistView.height/2+playlistView.contentY);
                            playlistView.currentIndex = center;
                            playlistView.currentIndex = index;
                            playlistItem.state = 'Selected';
                            playlist.contentYOffset += config.libraryFontSize*2;
                            playlistItem.selected = true;
                            resizeTimer.restart();
                        }
                        controller.playlistSelectionChanged(index);
                    }
                }
            }
        
            Text {
                id: queueText
                height: itemHeight/5
                color: main.selectionColor
                text: pl_queue
                anchors.left: parent.left
                anchors.top: parent.top
                anchors.leftMargin: playlistItem.queue == '' ? 0 : 10
                anchors.topMargin: units.dp(5)
                smooth: true
                font.pixelSize: config.smallFontSize
            }
    
            Text {
                id: titleText
                height: itemHeight/3
                color: main.textColor
                text: pl_artist == '' ? pl_title : pl_title+' - '
                anchors.left: queueText.right
                anchors.top: parent.top
                anchors.rightMargin: units.dp(5)
                anchors.leftMargin: units.gu(1)
                anchors.topMargin: itemHeight/2 - itemHeight/6 - units.dp(2)
                width: parent.width-dummyTitleText.queueWidth-units.gu(2)
                clip: false
                style: Text.Normal
                wrapMode: Text.NoWrap
                elide: Text.ElideRight
                smooth: true
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignLeft
                font.pixelSize: config.libraryFontSize
                focus: true
            }
    
            Text {
                id: dummyTitleText
                property int queueWidth: playlistItem.queue == '' ?  0 : queueText.width+10
                visible: false
                color: "#00000000"
                text: pl_artist == '' ? pl_title : pl_title+' - '
                style: Text.Normal
                wrapMode: Text.NoWrap
                font.pixelSize: config.libraryFontSize
            }
    
            Text {
                property int leftMargin: dummyTitleText.width+dummyTitleText.queueWidth+units.gu(1)
                id: artistText
                height: itemHeight/3
                color: main.textColor
                text: pl_artist
                anchors.leftMargin: leftMargin
                width: parent.width-leftMargin-units.gu(2)
                elide: Text.ElideRight
                anchors.top: parent.top
                font.pixelSize: config.libraryFontSize
                verticalAlignment: Text.AlignVCenter
                wrapMode: Text.NoWrap
                horizontalAlignment: Text.AlignLeft
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                style: Text.Normal
                smooth: true
            }
    
            Text {
                id: albumText
                opacity: 0
                height: itemHeight/3
                color: main.textColor
                text: pl_album
                anchors.leftMargin: units.gu(2)
                width: parent.width-2*itemHeight-units.gu(1)
                elide: Text.ElideRight
                anchors.bottom: parent.bottom
                font.pixelSize: config.normalFontSize
                verticalAlignment: Text.AlignVCenter
                clip: false
                wrapMode: Text.NoWrap
                horizontalAlignment: Text.AlignLeft
                anchors.left: parent.left
                style: Text.Normal
                smooth: true
                anchors.bottomMargin: itemHeight/8
            }
    
            Item {
                id: buttons            
                property int buttonSize: itemHeight-units.gu(2)
                width: parent.height
                height: parent.height-units.gu(2)
                anchors.rightMargin: units.gu(1)
                opacity: 0
                visible: false
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
    
                MyButton {
                    id: upButton
                    iconSource: "images/up.svg"
                    width: buttons.buttonSize
                    height: buttons.buttonSize
                    anchors.top: parent.top
                    anchors.left: parent.left
                    anchors.leftMargin: units.gu(1)
                    visible: index == 0 ? false : true
                    onClicked: {
                        controller.movePlaylistItem(index,false);
                        playlistItem.state = 'Selected';
                        resizeTimer.restart();
                        playlist.itemMoving = true;
                        playlist.restartMovingTimer();
                        if (playlistView.count*itemHeight+playlist.contentYOffset > playlistView.height) {
                            if (playlistView.contentY > itemHeight)
                                playlistView.contentY -= itemHeight;
                            else
                                playlistView.contentY = 0;
                        }
                    }
                }
    
                MyButton {
                    id: downButton
                    iconSource: "images/down.svg"
                    width: buttons.buttonSize
                    height: buttons.buttonSize
                    anchors.bottom: parent.bottom
                    anchors.left: parent.left
                    anchors.leftMargin: units.gu(1)
                    visible: index == playlistView.count-1 ? false : true
                    onClicked: {
                        controller.movePlaylistItem(index,true)
                        playlistItem.state = 'Selected'
                        resizeTimer.restart()
                        playlist.itemMoving = true;
                        playlist.restartMovingTimer();
                        playlistView.height, playlistView.count, itemHeight);
                        if (playlistView.count*itemHeight+playlist.contentYOffset > playlistView.height) {
                            if (playlistView.contentY < playlistView.count*itemHeight-playlistView.height+playlist.contentYOffset) {
                                playlistView.contentY += itemHeight;
                            }
                            else {
                                playlistView.contentY = playlistView.count*itemHeight-playlistView.height+playlist.contentYOffset;
                            }
                        }
                    }
                }
    
                MyButton {
                    id: delButton
                    width: buttons.buttonSize
                    height: buttons.buttonSize
                    iconSource: "images/delete.svg"
                    anchors.bottom: parent.bottom
                    anchors.rightMargin: units.gu(1)
                    anchors.right: parent.right
                    onClicked: {
                        playlist.updateIndex(-1);
                        playlistItem.state = 'Deleting';
                        playlist.contentYOffset -= config.libraryFontSize*2;
                        controller.removeFromPlaylist(index);
                    }
                }
    
                MyButton {
                    id: queueButton
                    iconSource: playlist.queueActive ? "" : "images/queue.svg"
                    width: buttons.buttonSize
                    height: buttons.buttonSize
                    anchors.top: parent.top
                    anchors.rightMargin: units.gu(1)
                    anchors.right: parent.right
                    onClicked: {
                        if (!playlist.queueActive) controller.addToQueue(index)
                    }
                }
            }
    
        }
    
        states: [
            State {
                name: "Current"
                when: playlistItem.ListView.isCurrentItem && !playlistItem.selected
                PropertyChanges { target: frame; border.color: main.selectionColor }
                PropertyChanges { target: titleText; color: main.selectionColor }
                PropertyChanges { target: artistText; color: main.selectionColor }
                PropertyChanges { target: albumText; color: "#00000000" }
            },
    
            State {
                name: "Selected"
                PropertyChanges { target: frame; border.color: playlistItem.ListView.isCurrentItem ? main.selectionColor : main.textColor }
                PropertyChanges { target: playlistItem; height: config.libraryFontSize*4 }
                PropertyChanges { target: buttons; opacity: 1; visible: true }
                PropertyChanges { target: itemMouseArea; anchors.rightMargin: itemHeight }
                
                PropertyChanges {
                    target: titleText;
                    color: playlistItem.ListView.isCurrentItem ? main.selectionColor : main.textColor;
                    anchors.topMargin: itemHeight/8+units.gu(2);
                    text: pl_title;
                    width: parent.width-2*itemHeight-dummyTitleText.queueWidth-units.gu(1)
                }
                
                PropertyChanges {
                    target: artistText;
                    color: playlistItem.ListView.isCurrentItem ? main.selectionColor : main.textColor;
                    width: parent.width-2*itemHeight-units.gu(1);
                    anchors.leftMargin: 10;
                }
                
                PropertyChanges {
                    target: albumText
                    color: playlistItem.ListView.isCurrentItem ? main.selectionColor : main.textColor;
                    anchors.bottomMargin: itemHeight/8+units.gu(2)
                    opacity: 1
                }            
            },
            
            State {
                name: "Deleting"
                PropertyChanges { target: frame; border.color: "#00000000" }
                PropertyChanges { target: playlistItem; height: config.libraryFontSize*4 }
    
                PropertyChanges {
                    target: buttons
                    opacity: 0
                    visible: false
                }
                PropertyChanges {
                    target: artistText
                    opacity: 0
                }
                PropertyChanges {
                    target: albumText
                    opacity: 0
                }
                PropertyChanges {
                    target: titleText
                    opacity: 0
                }
            }
        ]
    
        transitions: [
            Transition {
                from: "*"; to: "*"
                NumberAnimation { properties: "height"; easing.type: Easing.Linear; duration: 200 }
                NumberAnimation { properties: "opacity"; easing.type: Easing.Linear; duration: 200 }
                NumberAnimation { properties: "anchors.topMargin"; easing.type: Easing.Linear; duration: 200 }
                NumberAnimation { properties: "anchors.leftMargin"; easing.type: Easing.Linear; duration: 200 }
                ColorAnimation { duration: 200 }
            }
        ]
    }
    


  • I just found the problem! The property ListView.maximumFlickVelocity is to low for high resolution displays. So I have to adjust it according to screen DPI. Now it works perfectly.


Log in to reply