QML自定义侧边栏的设计与实现

在现代用户界面设计中,侧边栏作为一种常用的导航和信息展示手段,扮演着至关重要的角色。使用Qt Quick和QML在应用程序中实现一个既美观又实用的自定义侧边栏,可以极大地提升用户体验。本文将探讨如何在不涉及具体代码的前提下,设计和实现一个功能丰富的QML自定义侧边栏。 

 项目结构:

 

//SideBar.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Rectangle {
    property bool expand: false
    property bool compact: true

    height: parent.height
    width: expand ? parent.width : 0
    color: "transparent"


    Rectangle {

        id: menu
        width: compact ? 160 : 55
        height: parent.height
        x: expand ? 0 : -width
        Behavior on x {
            SpringAnimation {
                spring: 2
                damping: 0.2
            }
        }
        Behavior on width {
            NumberAnimation {
                duration: 400;
                easing.type: Easing.InOutExpo
            }
        }
        ListView {
            id: view
            width: parent.width
            height: parent.height
            clip: true
            headerPositioning: ListView.PullBackHeader
            footerPositioning: ListView.OverlayHeader
            model: models
            highlight: Rectangle {
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.leftMargin: 5
                anchors.rightMargin: 5
                radius: 5
                color: "#3d000000"
                z: 3
            }
            header: Rectangle {
                height: 70
                width: parent.width
                z: 3
                Button {
                    anchors.right: parent.right
                    anchors.verticalCenter: parent.verticalCenter
                    width: 10
                    height: 20
                    text: "点击收起"
                    onClicked: compact = !compact
                }
            }
            footer: Rectangle {
                height: 30
                width: parent.width
                z: 3
                Image {
                    id: settingIcon
                    anchors.verticalCenter: parent.verticalCenter
                    x: compact ? 15 : (parent.width - width) / 2

                    Behavior on x {
                        NumberAnimation {
                            duration: 400;
                            easing.type: Easing.InOutExpo
                        }
                    }
                    source: theme.settingIcon
                    sourceSize.height: 20
                    fillMode: Image.PreserveAspectFit
                }
                Text {
                    anchors.left: settingIcon.right
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.leftMargin: 10
                    text: "设置"
                    opacity: compact ? 1 : 0
                    Behavior on opacity {
                        NumberAnimation {
                            duration: 300
                        }
                    }
                }
                MouseArea {
                    anchors.fill: parent
                    onClicked: swipe.setCurrentIndex(swipe.count - 1)
                }
            }

            section.property: "cluster"
            section.criteria: ViewSection.FullString
            section.delegate: Rectangle {
                property bool expanded: true
                property bool hover: false
                id: wrapper
                clip: true
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.leftMargin: 5
                anchors.rightMargin: 5
                height: section ? 30 : 0
                opacity: section ? 1 : 0
                color: "transparent"
                Text {
                    text: section
                    scale: hover ? 1.3 : 1
                    font.bold: true
                    anchors.verticalCenter: parent.verticalCenter
                    x: compact ? 5 : (parent.width - width) / 2
                    Behavior on x {
                        NumberAnimation {
                            duration: 400
                            easing.type: Easing.InOutQuad
                        }
                    }
                    Behavior on scale {
                        NumberAnimation {
                            duration: 200
                        }
                    }
                }
                Image {
                    opacity: compact ? 1 : 0
                    anchors.right: parent.right
                    anchors.verticalCenter: parent.verticalCenter
                    source: theme.arrowRightIcon
                    sourceSize.height: 10
                    fillMode: Image.PreserveAspectFit
                    rotation: expanded ? 270 : 90
                    Behavior on rotation {
                        NumberAnimation {
                            duration: 200
                        }
                    }
                    Behavior on opacity {
                        NumberAnimation {
                            duration: 300
                        }
                    }
                }
                MouseArea {
                    id: mouse
                    anchors.fill: parent
                    hoverEnabled: true
                    onClicked: {
                        wrapper.expanded = !wrapper.expanded
                        for (let i = 0; i < models.count; ++i) {
                            const item = models.get(i);
                            if (item.cluster === section) item.visible = wrapper.expanded
                        }
                    }
                    onEntered: hover = true
                    onExited: hover = false
                }
            }

            delegate: Rectangle {
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.leftMargin: 5
                anchors.rightMargin: 5
                height: model.visible ? 30 : 0
                opacity: model.visible ? 1 : 0
                color: "transparent"
                Behavior on opacity {
                    NumberAnimation {
                        duration: 300;
                        easing.type: Easing.InOutExpo
                    }
                }
                Behavior on height {
                    NumberAnimation {
                        duration: 400;
                        easing.type: Easing.InOutQuad
                    }
                }
                Image {
                    id: itemIcon
                    anchors.verticalCenter: parent.verticalCenter
                    x: compact ? cluster ? 15 : 5 : (parent.width - width) / 2

                    Behavior on x {
                        NumberAnimation {
                            duration: 400;
                            easing.type: Easing.InOutExpo
                        }
                    }
                    source: icon
                    sourceSize.height: 20
                    fillMode: Image.PreserveAspectFit
                }
                Text {
                    anchors.left: itemIcon.right
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.leftMargin: 10
                    text: name
                    opacity: compact ? 1 : 0
                    Behavior on opacity {
                        NumberAnimation {
                            duration: 300
                        }
                    }
                }
                MouseArea {
                    anchors.fill: parent
                    onClicked: view.currentIndex = index
                }
            }
        }
    }
    ListModel {
        id: models
        // 如果 cluster 是空就当作根节点
        Component.onCompleted: {
            models.append({cluster: "来源", name: "相机", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "来源", name: "文件", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "来源", name: "合成光束", icon: "qrc:image/camera.svg", visible: true});

            models.append({cluster: "", name: "校正", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "", name: "采集", icon: "qrc:image/camera.svg", visible: true});

            models.append({cluster: "显示器", name: "调色板", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "显示器", name: "图形", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "显示器", name: "表面", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "显示器", name: "分区", icon: "qrc:image/camera.svg", visible: true});

            models.append({cluster: "", name: "测试报告", icon: "qrc:image/camera.svg", visible: true});
            models.append({cluster: "", name: "日志记录", icon: "qrc:image/camera.svg", visible: true});

        }
    }
    SwipeView {
        id: swipe
        clip: true
        anchors.left: menu.right
        anchors.right: parent.right
        height: parent.height
        currentIndex: view.currentIndex
        orientation: Qt.Vertical
        interactive: false
        Page {
            background: Rectangle {
                color: "#3d000000"
            }
            Flickable {
                anchors.fill: parent
                boundsBehavior: Flickable.DragOverBounds
                contentHeight: container.height

                Button {
                    width: 100
                    height: 30
                    text: "相机页面"
                }
            }
        }

        Page {
            background: Rectangle {
                color: "#3d0000ff"
            }
            Flickable {
                anchors.fill: parent
                boundsBehavior: Flickable.DragOverBounds
                contentHeight: container.height

                Button {
                    width: 100
                    height: 30
                    text: "文件页面"
                }
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现自定义标题栏可以通过使用QML中的Rectangle等组件来实现,并在其中添加需要的控件,例如按钮、标签、图标等。同时,为了实现与原生窗体相似的功能,可以使用QML中的Window组件,并将其属性设置为透明,然后在自定义标题栏中添加控件时,将它们放置在Window组件的边缘位置,以达到与原生窗体相似的效果。此外,还可以通过使用QML中的MouseArea组件来实现拖拽窗口的功能。具体实现方法可以参考以下示例代码: ``` import QtQuick 2.15 import QtQuick.Window 2.15 Window { visible: true width: 640 height: 480 title: "" Rectangle { id: titleBar width: parent.width height: 30 color: "#2d2d2d" MouseArea { anchors.fill: parent drag.target: parent } Label { text: "Custom Title Bar" font.bold: true color: "#ffffff" anchors.centerIn: parent } Button { text: "X" font.bold: true color: "#ffffff" anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter onClicked: { Qt.quit(); } } } Rectangle { id: content anchors.top: titleBar.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom color: "#ffffff" } } ``` 在这个示例中,我们使用了一个Rectangle组件作为自定义标题栏,并在其中添加了一个Label和一个Button。为了实现拖拽窗口的功能,我们在自定义标题栏上添加了一个MouseArea组件,并将drag.target设置为父组件。另外,我们还使用了另一个Rectangle组件作为窗口的内容区域,并通过anchors属性将其与自定义标题栏对齐。最后,我们将Window的title属性设置为空字符串,以隐藏原生窗口的标题栏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值