本章与大家一起开发 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 [email protected]
* @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 界面。
运行项目效果如下。因为我们还没有给滑动页面类加内容。所以是无法滑动的。继续往下
看。