Search code examples
qtqmlqtquick2qtquickcontrols2

How to implement Master Details View in Qt/QML (part 2)


I previously asked how to implement a Master Details View in Qt/QML here: How to implement a master-details view Qt/QML on an Android tablet?.

Having continued working on this, I came out with the following mockup QML layout:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.4

Item {
    y: 50
    Layout.fillHeight: true
    width: appWindow.width

    RowLayout {
        id: mainLayout
        anchors.fill: parent
        ListModel {
            id: navigation

            ListElement {
                item: "Item 1"
            }
            ListElement {
                item: "Item 2"
            }
            ListElement {
                item: "Item 3"
            }
            ListElement {
                item: "Item 4"
            }
            ListElement {
                item: "Item 5"
            }
            ListElement {
                item: "Item 6"
            }
            ListElement {
                item: "Item 7"
            }
            ListElement {
                item: "Item 8"
            }
            ListElement {
                item: "Item 9"
            }
            ListElement {
                item: "Item 10"
            }
            ListElement {
                item: "Item 11"
            }

        }
        ScrollView{
            Layout.fillHeight: true
            verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
            horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff

            ListView {
                id: listview
                Layout.fillHeight: true
                Layout.preferredWidth: 300
                contentWidth: 300
                model: navigation
                delegate: Rectangle {
                    id: wrapper
                    width: 300
                    height: 50
                    Text {
                        id: itemInfo
                        text: item
                        color: "red"
                    }
                }
            }
        }



        Rectangle {
            x: 300
            y: 50
            Layout.preferredWidth: appWindow.width - listview.width-4
            height: appWindow.height - 50
            color: "green"
            border.width: 1
        }
    }
}

The master view is essentially a ListView with a number of items (each item represents a selectable element, which will trigger an update of the details view, which is currently represented by the green rectangle (see attached screenshot below)

At the moment I am still having a couple of issues with the following points:

  • How should I modify the Layout so that the ListView covers the entire screen height?

  • When I "scroll" through the ListView, I have noticed a lot of screen flickering? How can I minimize this?

  • How can I prevent the entire upper status bar (where device system information such as battery charge is shown) from being displayed?

enter image description here

Edit: Modified the code by adding the ListView in a ScrollView. In this case, the ScrollView's height is the same as the screen height, which is also what I wanted (minus a 50 offset at the top, see Figure below). I think that the ListView is behaving as expected and not occupying more space that its items.

enter image description here

What needs to be achieved now is to change the Background color of the SrollView so that it matches the ListView color. In that case it will appear as if the ListView is occupying the entire space.


Solution

  • I am a bit clueless, how it comes, that you consider the ScrollView to be needed.

    I removed it from your example, added clipping to the ListView and I was done.

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    
    ApplicationWindow
    {
        id: appWindow
        width: 1024
        height: 800
        visible: true
    
        Item {
            y: 50
            Layout.fillHeight: true
            width: appWindow.width
    
            RowLayout {
                id: mainLayout
                anchors.fill: parent
                ListModel {
                    id: navigation
                    ListElement { item: "Item 1" }
                    Component.onCompleted: {
                        for (var i = 2; i < 50; i++) append({ item: 'Item' + i })
                    }
                }
    
                ListView {
                    id: listview
                    Layout.fillHeight: true
                    Layout.preferredWidth: 300
                    contentWidth: 300
                    model: navigation
                    clip: true //<--- Add this, so there won't be no elements outside the ListView-area
                    delegate: Rectangle {
                        id: wrapper
                        width: 300
                        height: 50
                        Text {
                            id: itemInfo
                            text: item
                            color: "red"
                        }
                    }
                }
    
                Rectangle {
                    x: 300
                    y: 50
                    Layout.preferredWidth: appWindow.width - listview.width-4
                    height: appWindow.height - 50
                    color: "green"
                    border.width: 1
                }
            }
        }
    }
    

    There are a few things you might misunderstand:

    1. The ListView provides no background. If you want such, you need to draw something behind it, e.g. a Rectangle
    2. The ListView does not provide ScrollBars by itself. You need to add them like this:

      ScrollBar.vertical: ScrollBar { }

    3. The ScrollBar has no native style. And the handle will disapear by default. You can find more than one question here, on how to style a ScrollBar.

    4. If you don't clip the ListView you will see some elements protruding the ListView and suddenly disappear. If you have nothing that covers this anyway, you should set clip: true