How to implement Master Details View in Qt/QML (pa

2019-09-06 03:04发布

问题:

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?

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.

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.

回答1:

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


回答2:

In order to hide the status bar, the easiest thing to do is to specify a theme and apply it in the manifest file. Other solutions require modifying the activity and such.

In yourApp/android/res/values create a theme.xml with the following content:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
    </style>
</resources>

Then in the manifest, on the same line where you added the screen orientation, add the theme:

android:theme="@style/AppTheme"

And in your main window use Window.FullScreen visibility instead of maximized.

For the layouting, it appears you could do with less. There is nothing wrong with Layout, just IMO it is more suited to standard scalable "micro" GUI elements like buttons and such rather than custom macro elements. Here is a condensed but functional example:

Row {
  anchors.fill: parent
  ListView {
    id: lv
    width: 200
    height: parent.height
    model: 30
    delegate: Rectangle {
      width: 200
      height: 50
      color: index == lv.currentIndex ? "lightgray" : "white"
      Text {
        text: "item " + index
        color: "red"
        anchors.centerIn: parent
      }
      MouseArea {
        anchors.fill: parent
        onClicked: lv.currentIndex = index
      }
    }
    Rectangle {
      anchors.right: parent.right
      width: 5
      height: parent.height * parent.visibleArea.heightRatio
      color: "grey"
      y: parent.height * parent.visibleArea.yPosition
    }
  }
  Rectangle {
    width: parent.width - lv.width
    height: parent.height
    color: "green"
    Text {
      anchors.centerIn: parent
      text: "selected item n" + lv.currentIndex
      color: "white"
      font.pointSize: 15
    }
  }
}

The result:

Although it is not exactly clear the reason you offset things vertically, if you want to have the free space at the top, simply don't fill the entire parent with the root Rowelement but rather size it accordingly.



回答3:

  1. For your ListView to take all the height, you can simply set it to fill the height of the layout. However make sure the Layout (and its parent in your case) have the right size too. Size defaults to (0,0) for Item in QML.

    ListView {
        id: listview
        //...
        Layout.fillHeight: true
        //...
    }
    
  2. Regarding the "flickering", you can try increasing the ListView cacheBuffer property, which corresponds to the content height, in pixels, which is preloaded. However if this is really flickering, there's probably little you can do.

Flickering appears when display is refreshed with the wrong timings regarding screen refresh rate, and typically solved by using multiple buffers and/or synchronization. QtQuick hides all this complexity and uses OpenGL for rendering, but I didn't saw (yet) any flickering on Android with recent Qt versions.

  1. You can remove the status bar by editing the Android manifest file as explained in this other post, or worse case, through JNI.