29. QML实现抽屉式选项窗口

1. 说明

最近使用Unity中的可视化编程组件Visual Scripting时,发现在组件设计窗口的鼠标右键菜单项效果很好,在有限的区域内能够展示很多内容,所以突发奇想使用QML语言自己大致实现了一下,效果还行,特记录在此,方便以后使用。
Unity本身效果:

unity中的右键菜单窗口


自己实现的效果:

类似抽屉式窗口设计

2. 实现思路

在内容呈现上是使用ListView空间展示每一个页面中的内容,页面切换是利用动态加载的方式创建每一个子页面,并使用动画过渡达到页面切换时的位移效果。
使用这种方式,理论上可以在页面中无限制的进行嵌套。

2.1 主要代码如下:

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    color: Qt.rgba(0,0,0,0.4)

    property QtObject childObj

    Rectangle{
        id:container
        width: 200
        height: 400
        anchors.centerIn: parent
        color:"#202020"
        clip:true

        Rectangle{
            id:titleRec
            width: parent.width
            height: 20
            color: /*"#3C3C3C"*/Qt.rgba(0,0,0,0)
            Text{
                id:titleTxt
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.verticalCenter: parent.verticalCenter
                text: "Node"
                color: "white"
            }
            Text{
                id:rtnTxt
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10
                text: "<"
                color: "white"
                visible: titleTxt.text == "Node" ? false : true
                MouseArea{
                    anchors.fill: parent
                    onClicked: {
                        childObj.animout.start()
                        childObj = null
                        titleTxt.text = "Node"
                    }
                }
            }
        }

        ListView{
            id:view
            width: parent.width
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: titleRec.bottom
            anchors.topMargin: 5
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 5
            spacing: 10
            highlight:Rectangle{
                anchors.horizontalCenter: parent.horizontalCenter
                width: parent.width * 0.9
                height: 30
                color: "#3E5F96"
            }
            highlightFollowsCurrentItem: true
            highlightMoveDuration: 1
            focus:true

            model:ListModel{
                ListElement {
                    name: "ScriptMachine"
                }
                ListElement {
                    name: "Codebase"
                }
                ListElement {
                    name: "Collections"
                }
                ListElement {
                    name: "Contorl"
                }
                ListElement {
                    name: "Events"
                }
                ListElement {
                    name: "Graphs"
                }
            }
            delegate: Rectangle{
                id:viewdel
                anchors.horizontalCenter: parent.horizontalCenter
                width: parent.width * 0.9
                height: 30
                color: /*"#3C3C3C"*/Qt.rgba(0,0,0,0)

                Text{
                    id:curLayerName
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
                    anchors.leftMargin: 10
                    text:name
                    color: "white"
                }
                Text{
                    id:curLayerIndicator
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.right: parent.right
                    anchors.rightMargin: 10
                    text:">"
                    color: "white"
                }
                MouseArea{
                    id:curLayerMouse
                    anchors.fill: parent
                    hoverEnabled: true
                    onEntered: {
                        view.currentIndex = index
                    }
                    onClicked: {
                        switch(curLayerName.text){
                        case ("ScriptMachine"):
                            titleTxt.text = curLayerName.text
                            var SM = Qt.createComponent("qrc:/ScriptMachine.qml")//针对不同的选项,创建不同的页面
                            childObj = SM.createObject(viewdel.parent.parent)
                            break
                        case ("Codebase"):
                            titleTxt.text = curLayerName.text
                            var CB = Qt.createComponent("qrc:/CodeBase.qml")
                            childObj = CB.createObject(viewdel.parent.parent)
                            break
                        }
                    }
                }

            }

        }
    }

}

上面的代码是主页面的,对于子页面也可以利用这种方式去创建自己的子页面,从而实现无限制的嵌套,不过要注意页面的切换逻辑不要搞混乱了。在上面代码中有个switch … case结构用于选择创建的子页面,这里只弄了两个,在每个子页面中需要设置一个动画,用于控制子页面的进入和退出,子页面的大致代码如下:
ScriptMachine.qml

import QtQuick 2.15

Rectangle {
    id:root
    width: parent.width
    height: parent.height
    color: "#202020"

    property alias animout:animOut

    ListView{
        id:view
        width: parent.width
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: root.top
        anchors.topMargin: 5
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 5
        spacing: 10
        highlight:Rectangle{
            anchors.horizontalCenter: parent.horizontalCenter
            width: parent.width * 0.9
            height: 30
            color: "#3E5F96"
        }
        highlightFollowsCurrentItem: true
        highlightMoveDuration: 1
        focus:true

        model:ListModel{
            ListElement {
                name: "Game Object"
            }
            ListElement {
                name: "Transform"
            }
        }
        delegate: Rectangle{
            id:viewdel
            anchors.horizontalCenter: parent.horizontalCenter
            width: parent.width * 0.9
            height: 30
            color: /*"#3C3C3C"*/Qt.rgba(0,0,0,0)

            Text{
                id:curLayerName
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10
                text:name
                color: "white"
            }
            Text{
                id:curLayerIndicator
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                anchors.rightMargin: 10
                text:">"
                color: "white"
            }
            MouseArea{
                id:curLayerMouse
                anchors.fill: parent
                hoverEnabled: true
                onEntered: {
                    view.currentIndex = index
                }
                onClicked: {
                    //这里可以使用switch...case结构创建子页面的子页面进行嵌套
                    switch(curLayerName.text){
                    case (""):

                        break
                    }
                }
            }

        }

    }
    //下面的动画用于控制当前页面的进入和退出效果
    NumberAnimation {
        id: animIn
        running: false
        target: root
        property: "x"
        from:root.parent.width;
        to:root.parent.x
        duration: 200;
        easing.type: Easing.InOutQuad
    }
    NumberAnimation {
        id: animOut
        running: false
        target: root
        property: "x"
        from:root.parent.x
        to:root.parent.width
        duration: 200;
        easing.type: Easing.InOutQuad
        onStopped: {
            root.destroy()//撤销动画执行完毕后销毁页面,防止占用内存
        }
    }

    Component.onCompleted: {
        animIn.start()
    }
}

整体代码已上传到gitee仓库中,需要的可以自行下载:
抽屉式窗口页面gitee仓库地址

你可能感兴趣的:(QT_QML自封装控件库,QML常用技巧汇总,QML,抽屉式窗口设计)