QT学习笔记( APP 主界面开发项目\滑动界面的设计)

news2024/10/7 2:17:58

APP 主界面开发项目

本章与大家一起开发 APP 主界面。Qt C++提供了像 QStackedWdget 与 QTableView 这种控
件可以方便的切换页面,但是这种切换页面的方法比较生硬,不能像手机一样滑动,往往这种
界面就会给用户较差的体验感。所以在传统的 Qt C++里(Qt Quick 除外),Qt 没有提供能够滑
动的页面界面。如今是移动设备到处都是,指尖触控交互,好的操作界面能给用户优越的体验
感。

在 Qt C++编程滑动屏幕界面这方面里,笔者也参考过许多网上的文章。发现很多都是使用

QPainter 结合 QMouseMove 事件来重绘屏幕或者移动屏幕的,这种方式代码量较长,而且不容
易移植。很多都是固定了界面的页数及大小,不能使用布局等等。于是笔者结合自己的开发经
验自己写一个滑动界面的类,可以很方便的增加页面,能跟随页面的大小变化而变化,是笔者
原创的一个作品,可以方便大家移植到需要写 APP 主界面的程序里。同时笔者也花了时间写了
一个好看的车载音乐主界面(只有界面,非功能的实现,笔者模仿网上的车载界面,用 Qt 实现
的),读者可以参考源代码来开发自己的界面。同时也是笔者带领读者开发 APP 主界面的入门
操作。不过这个入门操作是相当有难度了!因为这个界面比较复杂,内容较多。读者主要了解
下 26.1 小节滑动界面的使用,然后自己写一个只带一两个按钮的界面自已经测试一下效果滑动
效果,再细读源码。源码不细节讲解,最好的方式就是阅读源码理解整个流程。

滑动界面

本节代码程序是滑动页面的设计,下面贴出主程序代码。然后介绍实现思路。
项目路径为 4/03_appmainview/slidepage/slidepage.pro。

 /****************************************************************** 
 Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. 
 * @projectName slidepage 
 * @brief slidepage.cpp 
 * @author Deng Zhimao 
 * @email 1252699831@qq.com 
 * @net www.openedv.com 
 * @date 2021-06-09 
 *******************************************************************/ 

1 #include "slidepage.h" 

2 #include <QDebug> 

3 #include <QPropertyAnimation> 

4 

5 SlidePage::SlidePage(QWidget *parent): 

6 QWidget(parent), 

7 pageIndex(0), 

8 pageCount(0), 

9 draggingFlag(false) 

10 { 

11 pageIndicator.clear(); 

12 this->setMinimumSize(400, 300); 

13 this->setAttribute(Qt::WA_TranslucentBackground, true); 

14 

15 scrollArea = new QScrollArea(this); 

16 scrollArea->setAlignment(Qt::AlignCenter); 

17 

18 mainWidget = new QWidget(); 

19 mainWidget->setStyleSheet("background: transparent"); 

20 

21 scrollArea->setWidget(mainWidget); 

22 scrollArea->setStyleSheet("background: transparent"); 

23 

24 bottomWidget = new QWidget(this); 

25 bottomWidget->setStyleSheet("background: transparent"); 

26 

27 bottomHBoxLayout = new QHBoxLayout(); 

28 bottomWidget->setLayout(bottomHBoxLayout); 

29 bottomHBoxLayout->setContentsMargins(0, 0, 0, 0); 

30 bottomHBoxLayout->setAlignment(Qt::AlignCenter); 

31 
32 /* 关闭滚动条显示 */ 

33 scrollArea->setVerticalScrollBarPolicy( 

34 Qt::ScrollBarAlwaysOff); 

35 scrollArea->setHorizontalScrollBarPolicy( 

36 Qt::ScrollBarAlwaysOff); 

37 

38 /* 滚屏对象 */ 

39 scroller = QScroller::scroller(scrollArea); 

40 QScroller::ScrollerGestureType gesture = 
QScroller::LeftMouseButtonGesture; 

41 scroller->grabGesture(scrollArea, gesture); 

42 

43 /* 获取属性 */ 

44 QScrollerProperties properties = scroller->scrollerProperties(); 

45 

46 /* 设置滑动的时间,值越大,时间越短 */ 

47 properties.setScrollMetric(QScrollerProperties::SnapTime, 0.5); 

48 

49 /* 设置滑动速度 */ 

50 properties.setScrollMetric(QScrollerProperties::MinimumVelocity, 

1); 

51 scroller->setScrollerProperties(properties); 

52 

53 /* 布局 */ 

54 hBoxLayout = new QHBoxLayout(); 

55 

56 hBoxLayout->setContentsMargins(0, 0, 0, 0); 

57 hBoxLayout->setSpacing(0); 

58 

59 mainWidget->setLayout(hBoxLayout); 

60 

61 /* 定时器,用于判断用户是否是拖动屏幕,区分滑动,超过 300ms 表示拖动 */ 

62 timer = new QTimer(this); 

63 

64 connect(scrollArea->horizontalScrollBar(), 
SIGNAL(valueChanged(int)), this, SLOT(hScrollBarValueChanged(int))); 

65 connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, 
SLOT(onStateChanged(QScroller::State))); 

66 connect(timer, SIGNAL(timeout()), this, SLOT(onTimerTimeOut())); 

67 connect(this, SIGNAL(currentPageIndexChanged(int)), this, 
SLOT(onCurrentPageIndexChanged(int))); 

68 } 

69 

70 SlidePage::~SlidePage() 

71 { 

72 } 

73 

74 void SlidePage::addPage(QWidget *w) 

75 { 

76 /* 布局里添加页面 */ 

77 hBoxLayout->addWidget(w); 

78 /* 页数加一 */ 

79 pageCount++; 

80 QLabel *label = new QLabel(); 

81 label->setPixmap(QPixmap(":/icons/indicator1.png")); 

82 pageIndicator.append(label); 

83 bottomHBoxLayout->addWidget(label); 

84 } 

85 

86 void SlidePage::resizeEvent(QResizeEvent *event) 

87 { 

88 Q_UNUSED(event) 

89 scrollArea->resize(this->size()); 

90 /* mainWidget 需要比 scrollArea 小 */ 

91 mainWidget->resize(this->width() * pageCount, this->height() - 4); 

92 if (pageCount == 0) 

93 qDebug()<<"当前页面总数为 0,请使用 addPage()方法添加页面再使用!

"<<endl; 

94 else 

95 onCurrentPageIndexChanged(0); 

96 bottomWidget->setGeometry(0, this->height() - 20, this->width(), 

20); 

97 } 

98 

99 void SlidePage::hScrollBarValueChanged(int) 

100 { 

101 /* 滑动时判断当前页的下标 */ 

102 pageIndex= scrollArea->horizontalScrollBar()->value() / 
this->width(); 

103 pageIndex = scrollArea->horizontalScrollBar()->value() 

104 >= (pageIndex * this->width() + this->width() * 0.5) ? 
pageIndex + 1 : pageIndex; 

105 

106 } 

107 

108 void SlidePage::onStateChanged(QScroller::State state)

109 { 

110 static int pressedValue = 0; 

111 static int releasedValue = 0; 

112 static int currentPageIndex = 0; 

113 

114 /* 如果页面数为 0,返回,不做任何操作 */ 

115 if (pageCount == 0) 

116 return; 

117 

118 /* 松开 */ 

119 if (state == QScroller::Inactive) { 

120 /* 停止定时器,防止检测到界面是缓慢拖动状态 */ 

121 timer->stop(); 

122 /* 记录松开时的坐标 */ 

123 releasedValue = QCursor::pos().x(); 

124 

125 if (pressedValue == releasedValue) 

126 return; 

127 

128 /* 判断按下与松开的距离,首先先判断是不是拖动状态,如果是拖动状态,

pageIndex 不会变化 */ 

129 if (!draggingFlag) { 

130 if (pressedValue - releasedValue > 20 && currentPageIndex == 
pageIndex) 

131 pageIndex++; 

132 else 

133 pageIndex--; 

134 } 

135 

136 /* 页面下标判断 */ 

137 if (pageIndex == -1) 

138 pageIndex = 0; 

139 

140 if (pageIndex >= pageCount) 

141 pageIndex = pageCount - 1; 

142 

143 /* 动画 */ 

144 QPropertyAnimation *animation = new 
QPropertyAnimation(scrollArea->horizontalScrollBar(), "value"); 

145 animation->setDuration(200); 

146 
animation->setStartValue(scrollArea->horizontalScrollBar()->value()); 

147 animation->setEasingCurve(QEasingCurve::OutCurve); 

148 animation->setEndValue(pageIndex * this->width()); 

149 animation->start(); 

150 

151 if (currentPageIndex != pageIndex) { 

152 /* 发送当前页面的位置信号 */ 

153 emit currentPageIndexChanged(pageIndex); 

154 } 

155 

156 /* 重新赋值*/ 

157 pressedValue = 0; 

158 releasedValue = 0; 

159 draggingFlag = false; 

160 } 

161 

162 /* 按下 */ 

163 if (state == QScroller::Pressed) { 

164 pressedValue = QCursor::pos().x(); 

165 currentPageIndex = scrollArea->horizontalScrollBar()->value() / 
this->width(); 

166 /* 按下如果超过 300ms,表示用户在拖动 */ 

167 timer->start(300); 

168 } 

169 } 

170 

171 void SlidePage::onTimerTimeOut() 

172 { 

173 /* 拖动标志位 */ 

174 draggingFlag = true; 

175 timer->stop(); 

176 } 

177 

178 int SlidePage::getPageCount() 

179 { 

180 return pageCount; 

181 } 

182 

183 int SlidePage::getCurrentPageIndex() 

184 { 

185 return pageIndex; 

186 } 

187 

188 void SlidePage::onCurrentPageIndexChanged(int index) 

189 { 

190 for (int i = 0; i < pageIndicator.count(); i++) { 

191 if (i == index) 

192 
pageIndicator[i]->setPixmap(QPixmap(":/icons/indicator2.png")); 

193 else 

194 
pageIndicator[i]->setPixmap(QPixmap(":/icons/indicator1.png")); 

195 } 

196 } 

可以看到主程序代码量不多,仅 200 行不到就可以完成这样的滑动页面设计。(上面代码单
行较长,由于 Word 排版,会换行,若影响阅读,请打开源项目查看)。比网上用 QPainter 与

QMouseMove 实现页面滑动省了很多代码。
我们在第七章学习过了 QScrollArea,这是一个滑动的界面,初学时我们只知道它只能通过
两边的滑块来滚动界面。实际上,查 Qt 帮助文档资料得知,Qt 有一个 QScroller 类就可以实现
滑动界面。它能控制那种带滚动条的类的界面的滑动。详细可以看源码 39 行至 41 行。
滑动界面部分是由 QScroller 类控制。同时通过设置 QScrollArea-> horizontalScrollBar()这个
滚动条的 value 值就可以控制界面所处位置了。原理看似简单,详细需要查看程序,请读者带
着程序的原理通过细读程序源码来分析。
注意!运行这个项目时,您会发现这是一个空白的窗口!因为这只是一个滑动页面的类,
我们还没有在里面添加内容!所以接着往下看,我们先开发一页 APP 界面。
运行项目效果如下。因为我们还没有给滑动页面类加内容。所以是无法滑动的。继续往下
看。
在这里插入图片描述

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

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

相关文章

【GPT】让你事半功倍特别好用的5个GPT工具

文章目录前言一、现在还能开通ChatGPT4.0吗&#xff1f;二、推荐五款与ChatGPT的相关实用工具1.一款浏览器插件&#xff1a;ChatGPT for Google2.一款生成图片的AI工具&#xff1a;midjourney3.推荐两款AI自动生成PPT&#xff1a;闪击PPT、mindshow4.识别PFD文件内容对话&#…

信号与系统之《一文看懂傅里叶变换》

“傅里叶变换是一种非常有用的数学工具&#xff0c;它可以将一个复杂的信号分解成许多简单的频率成分。傅里叶变换在信号处理、图像处理、音乐、视频和通信等许多领域都有广泛的应用。相信大部分同学在毕业之后的一段时间之内都还没有理解到傅里叶变换的精髓&#xff0c;今天我…

【Java面试八股文宝典之MySQL篇】备战2023 查缺补漏 你越早准备 越早成功!!!——Day20

大家好&#xff0c;我是陶然同学&#xff0c;软件工程大三即将实习。认识我的朋友们知道&#xff0c;我是科班出身&#xff0c;学的还行&#xff0c;但是对面试掌握不够&#xff0c;所以我将用这100多天更新Java面试题&#x1f643;&#x1f643;。 不敢苟同&#xff0c;相信大…

(六)【软件设计师】计算机系统—原码反码补码移码

文章目录一、数据表示二、原码三、反码四、补码五、移码六、范围七、总结一、数据表示 各种数值在计算机中表示的形式称为机器数&#xff0c;其特点是采用二进制计数制&#xff0c;数的符号用0和1表示&#xff0c;小数点则隐含&#xff0c;表示不占位置。机器数对应的实际数值称…

使用zabbix监控avamar【二】

1、在“使用zabbix监控avamar【一】”中介绍了如何设置avamar端&#xff0c;并发送测试消息&#xff0c;本篇将介绍如何在zabbix server端进行配置。 2、在zabbix server的snmp trap日志文件中查找刚上传的报警信息 可以看到已经正常收到客户端信息。 3、在/etc/zabbix/snmpt…

2.0、Java继承与多态 - 方法重写与重载

2.0、Java继承与多态 - 方法重写与重载 重写&#xff08;Override&#xff09;&#xff1a;方法名一样&#xff0c;参数列表也一样 [ 重写也叫做覆写或者覆盖 ]&#xff1b; 重载&#xff08;Overload&#xff09;&#xff1a;方法名一样&#xff0c;但是参数列表不一样&#…

vue-cli 初始化工程

个人记录下vue-cli创建项目的步骤 卸载老版本的vue-cli (这不是必要的) npm uninstall vue-cli -g 如果本地使用 yarn的话,还需执行 yarn global remove vue-cli 安装全新的vue-cli npm install -g vue/cli 安装指定版本的vue-cli npm install -g vue/…

开源后台管理系统解决方案 boot-admin 简介

开源后台管理系统解决方案 boot-admin 简介介绍软件技术路线微服务架构图主要功能框图模块介绍介绍 boot-admin 是一款采用前后端分离架构模式的后台管理框架。系统提炼自实际项目&#xff0c;兼具RuoYi-Vue前端分离版和Ruoyi-Cloud微服务版功能与技术特点。 boot-admin 既有…

顶点程序经典案例——树木生长

返回目录 树木生长Shader一、介绍 大家好&#xff0c;我是阿赵。这次来做一个树木生长的Shader。 顶点程序作为整个渲染管线里面和片段程序并列的两大可控过程之一&#xff0c;一直存在感都比较低。我们平时制作的效果&#xff0c;很多都是在片段程序里面实现的计算&#xff0…

数据结构泛型

1.定义 先让我们看看官方是如何定义泛型的 是不是看起来不太容易&#xff0c;解释一下&#xff1a; 就是我们想有一种数据类型&#xff0c;它可以适用各种数据类型。从代码上讲就是对类型实现参数化。 2.引例 例&#xff1a;实现一个类&#xff0c;类中包含一个数组成员&…

直方图 颜色映射

文章目录hist map1. 原理2.灰度图3. 对于彩色图像4. 直方图规定化效果hist map 1. 原理 code:https://github.com/rossgoodwin/hmap 利用队列记录 hist src > tgt, src < tgt , src tgt的 索引。 然后&#xff0c;对于每个hist excess, 将其移动到 hist deficit 进行…

linux环境下,使用binlog模式恢复mysql数据(mysql数据库中的一张表误删了怎么找回?)。

linux环境下&#xff0c;使用binlog模式恢复mysql数据&#xff08;mysql数据库中的一张表误删了怎么找回&#xff1f;&#xff09;。 问题&#xff1a;linux中开启binlog模式下&#xff0c;mysql数据库中的一张表误删了怎么找回&#xff1f; 1.首先在mysql中查看是否开启binl…

气传导耳机是不是智商税?气传导耳机值得不值得入手?一文带你看懂

先说结论:并不是智商税。 目前的声音传播途径分为固体、气体和液体。而气传导耳机顾名思义&#xff0c;就是通过气体传声&#xff0c;这一点和我们日常使用的头戴式耳机、半入耳式入耳式耳机都是一样的&#xff0c;和我们日常生活中接触到最多的声音传播方式也是一样。 只不过…

动态规划概述

动态规划概述动态规划的两个要求&#xff1a; 1.最优子结构 例&#xff1a;现有一座10级台阶的楼梯&#xff0c;我们要从下往上走&#xff0c;每次只能跨一步&#xff0c;一步可以往上走1级或者2级台阶&#xff0c;请问一共有多少种解法呢&#xff1f; 台阶数12345678910走法数…

手动挡科目三道路驾驶技能考试及理论考试要点

路线每个驾校的科目三路线可能都不一样&#xff0c;但是考点基本差不多。我当时选的驾校是北京公交驾校&#xff0c;路线图如下&#xff1a;考试要点在考试大厅等待叫号&#xff0c;一般大屏都会公布xxx学员找xx号车考试&#xff0c;这边白色车是手动挡&#xff0c;灰色车是自动…

Web自动化测试-【Selenium环境部署Edge】

Selenium Web自动化测试工具 之前写过一篇关于自动化测试的博客&#xff0c;里面是有的chrome驱动&#xff0c;由于不适配缘故&#xff0c;更新以下Edge驱动。 自动化测试 Selenium环境部署 准备 Edge 浏览器准备 Edge 驱动包 a .查看自己的Edge浏览器版本&#xff08;浏览器版…

【论文解读】ConvNeXt V2: Co-designing and Scaling ConvNets with Masked Autoencoder

1. 本文贡献 提出了一个全卷积掩码的自动编码器框架和一个新的全局响应归一化&#xff08;GRN&#xff09;层 1.1 想法 本文的想法是希望能在 ConvNeXt 中使用MAE,但是MAE的设计架构是基于vision transformer的&#xff0c;与使用密集滑动窗口的标准ConvNets不兼容&#xf…

upload 通关pass16-pass20

1.pass16 白名单 二次渲染 需要先上传一个正常图片&#xff0c;然后下载下来&#xff0c;跟原图片进行比对&#xff0c;用010 16进制编辑器&#xff0c;把php代码放到没有改变的位置&#xff0c;即一样的地方 访问&#xff1a; 2.pass17 白名单 条件竞争 这题先是上传文件并…

音质蓝牙耳机哪款好用?2023公认音质好的四款蓝牙耳机推荐

现如今&#xff0c;蓝牙耳机越来越受欢迎&#xff0c;不少人在听歌、追剧、甚至是玩游戏的时候都会戴着它。最近看到很多人问&#xff0c;音质蓝牙耳机哪款好用&#xff1f;针对这个问题&#xff0c;我来给大家推荐四款公认音质好的蓝牙耳机&#xff0c;一起来看看吧。 一、南…

Nginx connect req access 模块

Nginx connect req access 模块演练 limig_conn模块&#xff1a;限制TCP连接数limit_req模块&#xff1a;限制请求频率access 模块&#xff08;allow/deny&#xff09;&#xff1a;限制ip段访问auth_request: 基于HTTP响应状态码做权限控制压测可以使用 postman的 run collect…