QML学习:使用QML实现抽屉式侧边栏菜单

文章目录

  • 前言
  • 一、环境配置
  • 二、实现步骤
  • 三、示例完整代码
  • 四、注意事项
  • 总结


前言

最近在进行QML的学习,发现一个比较有意思的交互设计:抽屉式侧边栏菜单,出于开发实战需求,最终实现了一个支持手势拖拽、弹性动画、蒙层效果和​​智能悬停检测​​的抽屉菜单,下面将我的示例内容进行展示,以便大家学习,如有错误之处,欢迎大家批评指正。

项目效果
请添加图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、环境配置

我的示例使用的是Qt5.14版本,需要确保包含 ​​Qt Quick Controls 2​​ 模块(安装时勾选Qt Quick Controls 2组件),需要在项目的.pro文件中添加:

QT += qml quick quickcontrols2

这里有详细的Qt安装和QML项目的创建步骤:Qt Creator的安装和Qml项目的创建
这里有QML的基础教程:QML快速入门(Quick Starter)

二、实现步骤

1、基础框架搭建

//通过ApplicationWindow创建主窗口
ApplicationWindow {id: mainWindowvisible: truewidth: 800height: 600title: "抽屉式菜单栏"......
}

2、顶部工具栏设计

//顶部工具栏
header: ToolBar {height: 50background: Rectangle {color: "#2196F3"   //蓝色背景}......//这里实现了汉堡菜单按钮,通过MouseArea检测悬停状态,并配合Timer实现​​智能延时关闭​​功能(详细代码见下文)......
}

3、抽屉菜单核心实现

//主内容区域
Item {id: mainContentanchors.fill: parent......//使用x坐标控制菜单位置,通过状态机(States)切换展开/收起状态,添加平滑动画过渡效果(详细代码见下文)......
}

4、高级交互功能

//边缘滑动手势检测,鼠标点击左侧区域后向右移动可以拉出菜单,双击也能展开菜单
MouseArea {id: drawerDragAreaanchors.left: parent.leftwidth: 20   //手势检测区域宽度height: parent.heightpropagateComposedEvents: true//拖拽起始位置的全局X坐标property real globalStartX: 0//鼠标按下事件处理onPressed: {globalStartX = mapToGlobal(mouseX, 0).xif(isDrawerOpen){mouse.accepted = false}}//位置变化事件处理onPositionChanged: {if (pressed) {//滑动距离超过25px时切换状态let currentGlobalX = mapToGlobal(mouseX, 0).xlet delta = currentGlobalX - globalStartXif (Math.abs(delta) > 10) {mainWindow.isDrawerOpen = delta > 0}}}//添加双击切换onDoubleClicked: isDrawerOpen = !isDrawerOpen
}

三、示例完整代码

1.main.qml

import QtQuick 2.14
import QtQuick.Controls 2.14ApplicationWindow {id: mainWindowvisible: truewidth: 800height: 600title: "抽屉式菜单栏"//当前选中的菜单项文本property string selectedMenuItem: "请选择菜单项"//是否正在与菜单栏交互property bool isMenuActive: false//菜单栏是否展开property bool isDrawerOpen: false//菜单栏宽度property int drawerWidth: 200//根据窗口宽度自动调整菜单栏宽度Component.onCompleted: {drawerWidth = Qt.binding(() => Math.min(200, mainWindow.width * 0.8))}//顶部工具栏header: ToolBar {height: 50background: Rectangle {color: "#2196F3"   //蓝色背景}Row {spacing: 20anchors.fill: parent//菜单入口按钮ToolButton {id: menuButtonhoverEnabled: true   //启用悬停检测​//是否悬停在按钮上property bool buttonHovered: false//按钮内容contentItem: Text {text: "☰"color: "white"font.pixelSize: 38}//鼠标交互MouseArea {anchors.fill: parenthoverEnabled: true//鼠标进入时展开菜单栏并修改悬停状态onEntered: {isDrawerOpen = truemenuButton.buttonHovered = true}//鼠标离开时重置状态并启动关闭计时器onExited: {menuButton.buttonHovered = falseif (!mainWindow.isMenuActive) {timer.restart()}}}//点击切换菜单栏状态//onClicked: isDrawerOpen = !isDrawerOpen//自动关闭菜单栏定时器Timer {id: timerinterval: 300   //延时300ms关闭onTriggered: {//双重验证是否满足关闭条件(未悬停在此按钮及菜单栏中)if (!mainWindow.isMenuActive && !menuButton.buttonHovered) {isDrawerOpen = false}}}}//应用标题Label {text: "我的应用"color: "white"font.bold: truefont.pixelSize: 18anchors.verticalCenter: parent.verticalCenter}}}//主内容区域Item {id: mainContentanchors.fill: parent//中央文本显示Text {id: contentTextanchors.centerIn: parenttext: mainWindow.selectedMenuItemfont.pixelSize: 24color: "#333333"}//遮罩层(展开菜单栏后出现)Rectangle {id: overlayanchors.fill: parentcolor: "#80000000"   //半透明黑色遮罩opacity: isDrawerOpen ? 1 : 0visible: opacity > 0enabled: visible//鼠标点击关闭菜单栏MouseArea {anchors.fill: parentonClicked: isDrawerOpen = false}//创建平滑的透明度渐变效果Behavior on opacity {NumberAnimation {duration: 200easing.type: Easing.OutQuad}}}//抽屉菜单栏容器Rectangle {id: drawerwidth: drawerWidthheight: parent.heightx: -drawerWidth  // 初始隐藏位置color: "#ffffff"//菜单栏悬停检测(注意这个需要放在ListView之前,否则动画效果异常)MouseArea {anchors.fill: parenthoverEnabled: truepropagateComposedEvents: true   //允许事件继续传递onEntered: mainWindow.isMenuActive = true//鼠标离开时重置状态并启动关闭计时器onExited: {mainWindow.isMenuActive = falsetimer.restart()}}//抽屉菜单列表ListView {anchors.fill: parentmodel: menuItems//菜单项模板delegate: ItemDelegate {id: menuItemwidth: drawerWidthheight: 50hoverEnabled: true//鼠标交互,处理悬停状态MouseArea {anchors.fill: parenthoverEnabled: truepropagateComposedEvents: true//设置为false,允许事件传递给ItemDelegate,不拦截点击事件onPressed: mouse.accepted = falseonEntered: mainWindow.isMenuActive = true//鼠标离开时重置状态并启动关闭计时器onExited: {//确保鼠标完全离开才更新状态if(!containsMouse) {mainWindow.isMenuActive = falsetimer.restart()}}}//背景样式background: Rectangle {color: menuItem.hovered ? "#2196F3" : "transparent"opacity: menuItem.hovered ? 0.6 : 0.3Behavior on color {ColorAnimation {duration: 150easing.type: Easing.OutCubic}}}//菜单项内容布局contentItem: Row {spacing: 10leftPadding: 10//图标Image {source: model.iconwidth: 24height: 24anchors.verticalCenter: parent.verticalCenter}//文本Text {text: model.titlecolor: "#444444"font.pixelSize: 16anchors.verticalCenter: parent.verticalCenter}}//点击事件处理onClicked: {console.log("切换到:", model.title)mainWindow.selectedMenuItem = model.title   //更新显示文本isDrawerOpen = false   //点击后自动关闭抽屉}}}//抽屉动画状态states: State {name: "opened"when: isDrawerOpenPropertyChanges {target: drawerx: 0}}//创建平滑缓动效果transitions: Transition {NumberAnimation {property: "x"duration: 300easing.type: Easing.InOutQuad}}}}//菜单项数据模型ListModel {id: menuItemsListElement { title: "首页"; icon: "qrc:/icons/home.png" }ListElement { title: "消息"; icon: "qrc:/icons/message.png" }ListElement { title: "设置"; icon: "qrc:/icons/settings.png" }ListElement { title: "个人中心"; icon: "qrc:/icons/profile.png" }}//边缘滑动手势检测MouseArea {id: drawerDragAreaanchors.left: parent.leftwidth: 20   //手势检测区域宽度height: parent.heightpropagateComposedEvents: true//拖拽起始位置的全局X坐标property real globalStartX: 0//鼠标按下事件处理onPressed: {globalStartX = mapToGlobal(mouseX, 0).xif(isDrawerOpen){mouse.accepted = false}}//位置变化事件处理onPositionChanged: {if (pressed) {//滑动距离超过25px时切换状态let currentGlobalX = mapToGlobal(mouseX, 0).xlet delta = currentGlobalX - globalStartXif (Math.abs(delta) > 10) {mainWindow.isDrawerOpen = delta > 0}}}//添加双击切换onDoubleClicked: isDrawerOpen = !isDrawerOpen}
}

四、注意事项

1、资源路径问题
其中菜单项数据模型中可以看到有图标资源,需要确保图标资源正确添加到.qrc文件,否则会显示为空白,在项目的源文件目录下添加了一个icons文件夹,其中有使用到的图片资源:

2、事件冲突处理​​
在代码中可以看到设置mouse.accepted = false,允许事件传递,避免阻断按钮点击事件


总结

这个示例中不仅实现了基础的抽屉菜单,还加入了​​手势交互​​、​​智能悬停检测等功能。可以看到QML的声明式语法让我们可以用少量代码实现复杂的动态效果,本文中实现这个示例也是比较简单的,要实现更复杂的功能还是需要不断的学习哦~


hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

参考文章:
Qt Creator的安装和Qml项目的创建
QML快速入门(Quick Starter)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78946.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

峰终定律——AI与思维模型【85】

一、定义 峰终定律思维模型是指人们对一段经历的评价主要取决于这段经历中的高峰时刻(无论是正向的还是负向的)以及结束时的感受,而不是整个经历的平均感受。也就是说,如果在一段体验的高峰和结尾阶段给人们留下积极、强烈的印象…

【补题】Codeforces Round 664 (Div. 1) A. Boboniu Chats with Du

题意:给出n,d,m三个值,分别代表,有多少个值ai,使用超过m的ai,需要禁言d天,如果不足也能使用,m代表区分点,问能得到最大的值有多少。 思路: …

单片机与上位机串口通信:原理、应用与实践

注:本文为 “单片机与上位机串口通信” 相关文章合辑。 略作重排,未整理去重。 如有内容异常,请看原文。 单片机与上位机的串行通信 饕餮 tt 于 2019 - 12 - 06 14:47:19 发布 写在前面 本文主要记录单片机通过 TXD、RXD 与上位机进行数据…

996引擎-人物模型(UIModel):创建内观时装备偏移问题

996引擎-人物模型(UIModel):创建内观时装备偏移问题 创建 人物模型(UIModel)问题参考资料创建 人物模型(UIModel) 90、91 是自定义剑甲的穿戴位置,因为需求只需要显示剑甲,所以下面创建人物模型时,只给了剑甲的id、特效。 function Controller:updateUI()-- 自定义收拾…

Python小程序:上班该做点摸鱼的事情

系统提醒 上班会忘记一些自己的事,所以你需要在上班的的时候突然给你弹窗,你就知道要做啥了 源码 这里有一个智能家居项目可以看看(开源) # -*- coding:utf-8 -*- """ 作者:YTQ 日期: 2025年04日29 21:51:24 """ impor…

centos安装部署配置kafka

1、解压到目录 tar -zxvf kafka_2.13-2.8.2.tgz -C /usr/local/kafka2.进入目录 cd /usr/local/kafka/kafka_2.13-2.8.23.查看版本(验证是否已解压) bin/kafka-topics.sh --version4.修改配置,注意:此配置中有一个默认的zookee…

深⼊理解指针(7)

1.函数指针变量的创建 在x86环境下: 我们发现:以函数是有地址的,函数名就是函数的地址,当然也可以通过& 函数名 的⽅式获得函数的地址。 如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数…

AdaBoost算法的原理及Python实现

一、概述 AdaBoost(Adaptive Boosting,自适应提升)是一种迭代式的集成学习算法,通过不断调整样本权重,提升弱学习器性能,最终集成为一个强学习器。它继承了 Boosting 的基本思想和关键机制,但在…

《PyTorch documentation》(PyTorch 文档)

PyTorch documentation(PyTorch 文档) PyTorch is an optimized tensor library for deep learning using GPUs and CPUs. (PyTorch是一个优化的张量库,用于使用GPU和CPU进行深度学习。) Features described in this documentation are classified by release status: (此…

Android学习总结之算法篇六(数组和栈)

括号匹配 public static boolean isValid(String s) {// 创建一个栈用于存储左括号Stack<Character> stack new Stack<>();// 遍历字符串中的每个字符for (char c : s.toCharArray()) {if (c ( || c [ || c {) {// 如果是左括号&#xff0c;将其压入栈中stack…

遗传算法(Genetic Algorithm,GA)

遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;是一种受生物进化理论启发的优化算法&#xff0c;通过模拟自然选择和遗传机制来搜索复杂问题的最优解。 ​​核心原理​​ ​​自然选择与适者生存​​&#xff1a;适应度高的个体更有可能繁殖&#xff0c;将…

消防应急物资智能调用立库:豪越科技助力消防“速战速决”

在消防救援的战场上&#xff0c;时间就是生命&#xff0c;每一秒都关乎着人民群众的生命财产安全。然而&#xff0c;在过去的紧急救援中&#xff0c;应急物资无法及时到位的情况时有发生&#xff0c;成为制约救援效率的关键难题&#xff0c;给救援工作带来了巨大的困境。 想象一…

【MySQL】数据类型和表的操作

目录 一. 常用的数据类型 1.数值类型 1.1 整形类型 1.2 浮点型类型 2.字符串类型 char和varchar的区别 如何选择char和varchar 3.日期类型 4.二进制类型 二. 表的操作 1.查看所有表 2.表的创建 3.查看表的结构 4.表的修改 4.1 添加新的列 4.2 修改表中现有的列 4…

涨薪技术|0到1学会性能测试第43课-apache status模块监控

前面的推文我们认识了apache目录结构与配置知识,今天我们继续来看下apache监控技术,究竟是怎么做性能监控的。后续文章都会系统分享干货,带大家从0到1学会性能测试。 Apache监控技术 关于apache监控通常会有两种方法: 一是:使用apache自带的status监控模块进行监控; 二是…

关于 MCP 的理论知识学习

文章目录 1. 写在最前面2. 基本概念2.1 Why MCP2.1.1 大模型访问的局限2.1.2 过渡阶段—Function Call2.1.3 当前阶段— MCP 3. 碎碎念4. 参考资料 1. 写在最前面 最近有一项任务是写旧版本迁移到新版本的支持文档&#xff0c;文档的编写是借助于 cursor 帮忙写的。但是实现的…

C++学习之路,从0到精通的征途:List类的模拟实现

目录 一.list的介绍 二.list的接口实现 1.结点 2.list结构 3.迭代器 &#xff08;1&#xff09;begin &#xff08;2&#xff09;end 4.修改 &#xff08;1&#xff09;insert &#xff08;2&#xff09;push_back &#xff08;3&#xff09;push_front &#xff0…

【游戏ai】从强化学习开始自学游戏ai-2 使用IPPO自博弈对抗pongv3环境

文章目录 前言一、环境设计二、动作设计三、状态设计四、神经网路设计五、效果展示其他问题总结 前言 本学期的大作业&#xff0c;要求完成多智能体PPO的乒乓球对抗环境&#xff0c;这里我使用IPPO的方法来实现。 正好之前做过这个单个PPO与pong环境内置的ai对抗的训练&#…

计算机考研精炼 操作系统

第 14 章 操作系统概述 14.1 基本概念 14.1.1 操作系统的基本概念 如图 14 - 1 所示&#xff0c;操作系统是计算机系统中的一个重要组成部分&#xff0c;它位于计算机硬件和用户程序&#xff08;用户&#xff09;之间&#xff0c;负责管理计算机的硬件资源&#xff0c;为用户和…

什么是基尔霍夫第一定律

基尔霍夫第一定律&#xff08;Kirchhoffs First Law&#xff09;&#xff0c;也称为基尔霍夫电流定律&#xff08;Kirchhoffs Current Law&#xff0c;简称 KCL&#xff09;&#xff0c;是电路分析中最基础的定律之一。它描述了电路中电流的守恒特性&#xff0c;适用于任何集总…

解决 RN Switch 组件在安卓端样式很丑的问题

解决此种问题的方式有很多 可以导入原生库react-native-switch 切图 (会缺少动画) 使用 js 组件 这里使用 js 绘制组件&#xff08;原生体验&#xff09;解决此类问题 Switch.tsx import React, { useEffect, useRef, useState } from react; import { Animated, Pressabl…