Qt Quick 示例 - 视图#

这是一个 QML 模型视图示例的集合。

../_images/qml-modelviews-example.png

视图 是一个包含小型 QML 示例的集合,这些示例与模型和视图功能相关。它们演示了如何使用 Qt Quick 视图类型来展示模型中的数据。有关更多信息,请访问 Qt Quick 中的模型和视图 页面。

运行示例#

要从 Qt Creator 中运行示例,请打开欢迎模式并从示例中选择示例。有关更多信息,请参阅建设和运行示例。

使用 GridView 和 PathView#

GridViewPathView 展示了如何使用这些类型来展示视图。

GridView {
    anchors.fill: parent
    cellWidth: 100
    cellHeight: 100
    focus: true
    model: appModel

    highlight: Rectangle {
        width: 80
        height: 80
        color: "lightsteelblue"
    }

    delegate: Item {
        required property string icon
        required property string name
        required property int index

        width: 100
        height: 100

        Image {
            id: myIcon
            y: 20
            anchors.horizontalCenter: parent.horizontalCenter
            source: parent.icon
        }
        Text {
            anchors {
                top: myIcon.bottom
                horizontalCenter: parent.horizontalCenter
            }
            text: parent.name
        }
        MouseArea {
            anchors.fill: parent
            onClicked: parent.GridView.view.currentIndex = parent.index
        }
    }
}

使用 Dynamic List#

Dynamic List 展示了 runtime 向 ListView 添加和删除操作时的动画。

ListView .onAdd 信号处理器在向视图添加新项时运行动画,当它们被删除时,ListView .onRemove 执行另一项动画。

Item {
    SequentialAnimation {
        id: addAnimation
        PropertyAction {
            target: delegateItem
            property: "height"
            value: 0
        }
        NumberAnimation {
            target: delegateItem
            property: "height"
            to: 80
            duration: 250
            easing.type: Easing.InOutQuad
        }
    }
    ListView.onAdd: addAnimation.start()

    SequentialAnimation {
        id: removeAnimation
        PropertyAction {
            target: delegateItem
            property: "ListView.delayRemove"
            value: true
        }
        NumberAnimation {
            target: delegateItem
            property: "height"
            to: 0
            duration: 250
            easing.type: Easing.InOutQuad
        }

        // Make sure delayRemove is set back to false so that the item can be destroyed
        PropertyAction {
            target: delegateItem
            property: "ListView.delayRemove"
            value: false
        }
    }
    ListView.onRemove: removeAnimation.start()
}

展开委托#

展开委托 展示了当被激活时展开的委托。

它有一个复杂委托,其大小和外观可以改变,可以挤出视图中的其他项目。

Item {
    id: recipe

    required property string title
    required property string picture
    required property string ingredients
    required property string method

    // Create a property to contain the visibility of the details.
    // We can bind multiple element's opacity to this one property,
    // rather than having a "PropertyChanges" line for each element we
    // want to fade.
    property real detailsOpacity : 0
MouseArea {
    anchors.fill: parent
    onClicked: recipe.state = 'Details';
}

// Lay out the page: picture, title and ingredients at the top, and method at the
// bottom.  Note that elements that should not be visible in the list
// mode have their opacity set to recipe.detailsOpacity.

Row {
    id: topLayout
    x: 10
    y: 10
    height: recipeImage.height
    width: parent.width
    spacing: 10

    Image {
        id: recipeImage
        width: 50
        height: 50
        source: recipe.picture
    }
Item {
    id: details
    x: 10
    width: parent.width - 20

    anchors {
        top: topLayout.bottom
        topMargin: 10
        bottom: parent.bottom
        bottomMargin: 10
    }
    opacity: recipe.detailsOpacity
    }

    // A button to close the detailed view, i.e. set the state back to default ('').
    TextButton {
        y: 10
        anchors {
            right: background.right
            rightMargin: 10
        }
        opacity: recipe.detailsOpacity
        text: qsTr("Close")

        onClicked: recipe.state = '';
    }

    states: State {
        name: "Details"

        PropertyChanges {
            background.color: "white"
            recipeImage {
                 // Make picture bigger
                width: 130
                height: 130
            }
            recipe {
                // Make details visible
                detailsOpacity: 1
                x: 0

                // Fill the entire list area with the detailed view
                height: listView.height
            }
        }

        // Move the list so that this item is at the top.
        PropertyChanges {
            recipe.ListView.view.contentY: recipe.y
            explicit: true;
        }

        // Disallow flicking while we're in detailed view
        PropertyChanges {
            recipe.ListView.view.interactive: false
        }
    }

    transitions: Transition {
        // Make the state changes smooth
        ParallelAnimation {
            ColorAnimation {
                property: "color"
                duration: 500
            }
            NumberAnimation {
                duration: 300
                properties: "detailsOpacity,x,contentY,height,width"
            }
        }
    }
}

使用 Highlight#

Highlight 展示了如何给 ListView 添加自定义高亮显示。

// Define a highlight with customized movement between items.
component HighlightBar : Rectangle {
    width: 200
    height: 50
    color: "#FFFF88"
    y: ListView.view.currentItem.y
    Behavior on y {
        SpringAnimation {
            spring: 2
            damping: 0.1
        }
    }
}

ListView {
    id: listView
    width: 200
    height: parent.height
    x: 30

    model: PetsModel { }
    delegate: PetDelegate { }
    focus: true

    // Set the highlight delegate. Note we must also set highlightFollowsCurrentItem
    // to false so the highlight delegate can control how the highlight is moved.
    highlight: HighlightBar { }
    highlightFollowsCurrentItem: false
}

使用 Highlight Ranges#

Highlight Ranges 展示了 ListView 的三种不同高亮范围模式。

Rectangle {
    id: root
    property int current: 0
    property bool increasing: true
    // Example index automation for convenience, disabled on click or tap
    SequentialAnimation {
        id: anim
        loops: -1
        running: true
        ScriptAction {
            script: if (root.increasing) {
                        root.current++;
                        if (root.current >= aModel.count -1) {
                            root.current = aModel.count - 1;
                            root.increasing = !root.increasing;
                        }
                    } else {
                        root.current--;
                        if (root.current <= 0) {
                            root.current = 0;
                            root.increasing = !root.increasing;
                        }
                    }
        }

        PauseAnimation {
            duration: 500
        }
    }
ListView {
    id: list1
    height: 50
    width: parent.width
    model: PetsModel {
        id: aModel
    }
    delegate: petDelegate
    orientation: ListView.Horizontal
    highlight: Rectangle {
        color: "lightsteelblue"
    }
    currentIndex: root.current
    onCurrentIndexChanged: root.current = currentIndex
    focus: true
}

ListView {
    id: list2
    y: 160
    height: 50
    width: parent.width
    model: PetsModel { }
    delegate: petDelegate
    orientation: ListView.Horizontal
    highlight: Rectangle {
        color: "yellow"
    }
    currentIndex: root.current
    preferredHighlightBegin: 80
    preferredHighlightEnd: 220
    highlightRangeMode: ListView.ApplyRange
}

ListView {
    id: list3
    y: 320
    height: 50
    width: parent.width
    model: PetsModel {}
    delegate: petDelegate
    orientation: ListView.Horizontal
    highlight: Rectangle { color: "yellow" }
    currentIndex: root.current
    onCurrentIndexChanged: root.current = currentIndex
    preferredHighlightBegin: 125
    preferredHighlightEnd: 125
    highlightRangeMode: ListView.StrictlyEnforceRange
}
}

使用 Sections#

Section 展示了可用于 ListView 的各种分区标题和页脚。

// The delegate for each section header
Component {
    id: sectionHeading
    Rectangle {
        width: ListView.view.width
        height: childrenRect.height
        color: "lightsteelblue"

        required property string section

        Text {
            text: parent.section
            font.bold: true
            font.pixelSize: 20
        }
    }
}

ListView {
    id: view
    anchors.top: parent.top
    anchors.bottom: buttonBar.top
    width: parent.width
    model: animalsModel
    delegate: Text {
        required property string name

        text: name
        font.pixelSize: 18
    }

    section.property: "size"
    section.criteria: ViewSection.FullString
    section.delegate: sectionHeading
}

使用 Packages#

Packages 使用 Package 类型在两个视图之间转换委托。

它有一个 Package 对象,该对象定义了每个视图的委托项,以及一个可以在委托之间传输的项目。

Package {
    id: delegate

    required property int upTo
    required property int index
    required property string display

    Text {
        id: listDelegate
        width: parent.width
        height: 25
        text: 'Empty'
        Package.name: 'list'
    }

    Text {
        id: gridDelegate
        width: parent.width / 2
        height: 50
        text: 'Empty'
        Package.name: 'grid'
    }

    Rectangle {
        id: wrapper
        width: parent?.width ?? 0
        height: 25
        color: 'lightsteelblue'

        Text {
            text: delegate.display
            anchors.centerIn: parent
        }
        state: delegate.upTo > delegate.index ? 'inGrid' : 'inList'
        states: [
            State {
                name: 'inList'
                ParentChange {
                    target: wrapper
                    parent: listDelegate
                }
            },
            State {
                name: 'inGrid'
                ParentChange {
                    target: wrapper
                    parent: gridDelegate
                    x: 0
                    y: 0
                    width: gridDelegate.width
                    height: gridDelegate.height
                }
            }
        ]

        transitions: [
            Transition {
                ParentAnimation {
                    NumberAnimation {
                        properties: 'x,y,width,height'
                        duration: 300
                    }
                }
            }
        ]
    }
}

DelegateModel 允许各个视图从共享的包委托中访问其特定的项目。

DelegateModel {
    id: visualModel
    delegate: Delegate {
        upTo: root.upTo
    }
    model: myModel
}

ListView {
    id: lv
    height: parent.height / 2
    width: parent.width

    model: visualModel.parts.list
}
GridView {
    y: parent.height / 2
    height: parent.height / 2
    width: parent.width
    cellWidth: width / 2
    cellHeight: 50
    model: visualModel.parts.grid
}

Draggable Selections 展示了使用 Package 将多个选定的委托组合在一起以在项目视图中进行拖放的方法。

Package {
    id: packageRoot

    required property var modelData

    MouseArea {
        id: visibleContainer
        Package.name: "visible"

        width: 64
        height: 64
        enabled: packageRoot.DelegateModel.inSelected

        drag.target: draggable

        Item {
            id: draggable

            Drag.active: visibleContainer.drag.active

使用 ObjectModel#

ObjectModel 使用 ObjectModel 作为模型,而不是 ListModel。

ObjectModel {
    id: itemModel

    Rectangle {
        width: view.width
        height: view.height
        color: "#FFFEF0"

        Text {
            anchors.centerIn: parent
            text: qsTr("Page 1")
            font.bold: true
        }

        Component.onDestruction: if (root.printDestruction) print("destroyed 1")
    }
    Rectangle {
        width: view.width
        height: view.height
        color: "#F0FFF7"

        Text {
            anchors.centerIn: parent
            text: qsTr("Page 2")
            font.bold: true
        }

        Component.onDestruction: if (root.printDestruction) print("destroyed 2")
    }
    Rectangle {
        width: view.width
        height: view.height
        color: "#F4F0FF"

        Text {
            anchors.centerIn: parent
            text: qsTr("Page 3")
            font.bold: true
        }

        Component.onDestruction: if (root.printDestruction) print("destroyed 3")
    }
}

ListView {
    id: view
    anchors {
        fill: parent
        bottomMargin: 30
    }
    model: itemModel
    preferredHighlightBegin: 0
    preferredHighlightEnd: 0
    highlightRangeMode: ListView.StrictlyEnforceRange
    orientation: ListView.Horizontal
    snapMode: ListView.SnapOneItem
    flickDeceleration: 2000
    cacheBuffer: 200
}

使用 Display Margins#

Display Margins 使用委托来显示项目,并实现了一个简单的标题和页脚组件。

示例项目 @ code.qt.io