搜索
bottom↓
回复: 0

《I.MX6U嵌入式Qt开发指南》第二十六章 APP主界面开发项目

[复制链接]

出0入234汤圆

发表于 2021-7-20 18:31:56 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-8-11 12:21 编辑

1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】《I.MX6U嵌入式Qt开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子Linux技术交流群:1027879335
1.png

2.jpg


3.png



第二十六章 APP主界面开发项目


       本章与大家一起开发APP主界面。Qt C++提供了像QStackedWdget与QTableView这种控件可以方便的切换页面,但是这种切换页面的方法比较生硬,不能像手机一样滑动,往往这种界面就会给用户较差的体验感。所以在传统的Qt C++里(Qt Quick除外),Qt没有提供能够滑动的页面界面。如今是移动设备到处都是,指尖触控交互,好的操作界面能给用户优越的体验感。
       在Qt C++编程滑动屏幕界面这方面里,编者也参考过许多网上的文章。发现很多都是使用QPainter结合QMouseMove事件来重绘屏幕或者移动屏幕的,这种方式代码量较长,而且不容易移植。很多都是固定了界面的页数及大小,不能使用布局等等。于是编者结合自己的开发经验自己写一个滑动界面的类,可以很方便的增加页面,能跟随页面的大小变化而变化,是编者原创的一个作品,可以方便大家移植到需要写APP主界面的程序里。同时编者也花了时间写了一个好看的车载音乐主界面(只有界面,非功能的实现,编者模仿网上的车载界面,用Qt实现的),读者可以参考源代码来开发自己的界面。同时也是编者带领读者开发APP主界面的入门操作。不过这个入门操作是相当有难度了!因为这个界面比较复杂,内容较多。读者主要了解下26.1小节滑动界面的使用,然后自己写一个只带一两个按钮的界面自已经测试一下效果滑动效果,再细读源码。源码不细节讲解,最好的方式就是阅读源码理解整个流程。

26.1 滑动界面
        本节代码程序是滑动页面的设计,下面贴出主程序代码。然后介绍实现思路。
        项目路径为4/03_appmainview/slidepage/slidepage.pro。
  1. <font size="2">    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   slidepage
  4.     * @brief         slidepage.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank" style="">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank" style="">www.openedv.com</a>
  8.     * @date           2021-06-09
  9.     *******************************************************************/
  10. 1   #include "slidepage.h"
  11. 2   #include <QDebug>
  12. 3   #include <QPropertyAnimation>
  13. 4  
  14. 5   SlidePage::SlidePage(QWidget *parent):
  15. 6       QWidget(parent),
  16. 7       pageIndex(0),
  17. 8       pageCount(0),
  18. 9       draggingFlag(false)
  19. 10  {
  20. 11      pageIndicator.clear();
  21. 12      this->setMinimumSize(400, 300);
  22. 13      this->setAttribute(Qt::WA_TranslucentBackground, true);
  23. 14
  24. 15      scrollArea = new QScrollArea(this);
  25. 16      scrollArea->setAlignment(Qt::AlignCenter);
  26. 17
  27. 18      mainWidget = new QWidget();
  28. 19      mainWidget->setStyleSheet("background: transparent");
  29. 20
  30. 21      scrollArea->setWidget(mainWidget);
  31. 22      scrollArea->setStyleSheet("background: transparent");
  32. 23
  33. 24      bottomWidget = new QWidget(this);
  34. 25      bottomWidget->setStyleSheet("background: transparent");
  35. 26
  36. 27      bottomHBoxLayout = new QHBoxLayout();
  37. 28      bottomWidget->setLayout(bottomHBoxLayout);
  38. 29      bottomHBoxLayout->setContentsMargins(0, 0, 0, 0);
  39. 30      bottomHBoxLayout->setAlignment(Qt::AlignCenter);
  40. 31
  41. 32      /* 关闭滚动条显示 */
  42. 33      scrollArea->setVerticalScrollBarPolicy(
  43. 34                  Qt::ScrollBarAlwaysOff);
  44. 35      scrollArea->setHorizontalScrollBarPolicy(
  45. 36                  Qt::ScrollBarAlwaysOff);
  46. 37
  47. 38      /* 滚屏对象 */
  48. 39      scroller = QScroller::scroller(scrollArea);
  49. 40      QScroller::ScrollerGestureType gesture = QScroller::LeftMouseButtonGesture;
  50. 41      scroller->grabGesture(scrollArea, gesture);
  51. 42
  52. 43      /* 获取属性 */
  53. 44      QScrollerProperties properties = scroller->scrollerProperties();
  54. 45
  55. 46      /* 设置滑动的时间,值越大,时间越短 */
  56. 47      properties.setScrollMetric(QScrollerProperties::SnapTime, 0.5);
  57. 48
  58. 49      /* 设置滑动速度 */
  59. 50      properties.setScrollMetric(QScrollerProperties::MinimumVelocity, 1);
  60. 51      scroller->setScrollerProperties(properties);
  61. 52
  62. 53      /* 布局 */
  63. 54      hBoxLayout = new QHBoxLayout();
  64. 55
  65. 56      hBoxLayout->setContentsMargins(0, 0, 0, 0);
  66. 57      hBoxLayout->setSpacing(0);
  67. 58
  68. 59      mainWidget->setLayout(hBoxLayout);
  69. 60
  70. 61      /* 定时器,用于判断用户是否是拖动屏幕,区分滑动,超过300ms表示拖动 */
  71. 62      timer = new QTimer(this);
  72. 63
  73. 64      connect(scrollArea->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hScrollBarValueChanged(int)));
  74. 65      connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(onStateChanged(QScroller::State)));
  75. 66      connect(timer, SIGNAL(timeout()), this, SLOT(onTimerTimeOut()));
  76. 67      connect(this, SIGNAL(currentPageIndexChanged(int)), this, SLOT(onCurrentPageIndexChanged(int)));
  77. 68  }
  78. 69
  79. 70  SlidePage::~SlidePage()
  80. 71  {
  81. 72  }
  82. 73
  83. 74  void SlidePage::addPage(QWidget *w)
  84. 75  {
  85. 76      /* 布局里添加页面 */
  86. 77      hBoxLayout->addWidget(w);
  87. 78      /* 页数加一 */
  88. 79      pageCount++;
  89. 80      QLabel *label = new QLabel();
  90. 81      label->setPixmap(QPixmap(":/icons/indicator1.png"));
  91. 82      pageIndicator.append(label);
  92. 83      bottomHBoxLayout->addWidget(label);
  93. 84  }
  94. 85
  95. 86  void SlidePage::resizeEvent(QResizeEvent *event)
  96. 87  {
  97. 88      Q_UNUSED(event)
  98. 89      scrollArea->resize(this->size());
  99. 90      /* mainWidget需要比scrollArea小 */
  100. 91      mainWidget->resize(this->width() * pageCount, this->height() - 4);
  101. 92      if (pageCount == 0)
  102. 93          qDebug()<<"当前页面总数为0,请使用addPage()方法添加页面再使用!"<<endl;
  103. 94      else
  104. 95          onCurrentPageIndexChanged(0);
  105. 96      bottomWidget->setGeometry(0, this->height() - 20, this->width(), 20);
  106. 97  }
  107. 98
  108. 99  void SlidePage::hScrollBarValueChanged(int)
  109. 100 {
  110. 101     /* 滑动时判断当前页的下标 */
  111. 102     pageIndex= scrollArea->horizontalScrollBar()->value() / this->width();
  112. 103     pageIndex = scrollArea->horizontalScrollBar()->value()
  113. 104             >= (pageIndex * this->width() + this->width() * 0.5) ? pageIndex + 1 : pageIndex;
  114. 105
  115. 106 }
  116. 107
  117. 108 void SlidePage::onStateChanged(QScroller::State state)
  118. 109 {
  119. 110     static int pressedValue = 0;
  120. 111     static int releasedValue = 0;
  121. 112     static int currentPageIndex = 0;
  122. 113
  123. 114     /* 如果页面数为0,返回,不做任何操作 */
  124. 115     if (pageCount == 0)
  125. 116         return;
  126. 117
  127. 118     /* 松开 */
  128. 119     if (state == QScroller::Inactive) {
  129. 120         /* 停止定时器,防止检测到界面是缓慢拖动状态 */
  130. 121         timer->stop();
  131. 122         /* 记录松开时的坐标 */
  132. 123         releasedValue = QCursor::pos().x();
  133. 124
  134. 125         if (pressedValue == releasedValue)
  135. 126             return;
  136. 127
  137. 128         /* 判断按下与松开的距离,首先先判断是不是拖动状态,如果是拖动状态,pageIndex不会变化 */
  138. 129         if (!draggingFlag) {
  139. 130             if (pressedValue - releasedValue > 20 && currentPageIndex == pageIndex)
  140. 131                 pageIndex++;
  141. 132             else
  142. 133                 pageIndex--;
  143. 134         }
  144. 135
  145. 136         /* 页面下标判断 */
  146. 137         if (pageIndex == -1)
  147. 138             pageIndex = 0;
  148. 139
  149. 140         if (pageIndex >= pageCount)
  150. 141             pageIndex = pageCount - 1;
  151. 142
  152. 143         /* 动画 */
  153. 144         QPropertyAnimation *animation = new QPropertyAnimation(scrollArea->horizontalScrollBar(), "value");
  154. 145         animation->setDuration(200);
  155. 146         animation->setStartValue(scrollArea->horizontalScrollBar()->value());
  156. 147         animation->setEasingCurve(QEasingCurve::OutCurve);
  157. 148         animation->setEndValue(pageIndex * this->width());
  158. 149         animation->start();
  159. 150
  160. 151         if (currentPageIndex != pageIndex) {
  161. 152             /* 发送当前页面的位置信号 */
  162. 153             emit currentPageIndexChanged(pageIndex);
  163. 154         }
  164. 155
  165. 156         /* 重新赋值*/
  166. 157         pressedValue = 0;
  167. 158         releasedValue = 0;
  168. 159         draggingFlag = false;
  169. 160     }
  170. 161
  171. 162     /* 按下 */
  172. 163     if (state == QScroller::Pressed) {
  173. 164         pressedValue = QCursor::pos().x();
  174. 165         currentPageIndex = scrollArea->horizontalScrollBar()->value() / this->width();
  175. 166         /* 按下如果超过300ms,表示用户在拖动 */
  176. 167         timer->start(300);
  177. 168     }
  178. 169 }
  179. 170
  180. 171 void SlidePage::onTimerTimeOut()
  181. 172 {
  182. 173     /* 拖动标志位 */
  183. 174     draggingFlag = true;
  184. 175     timer->stop();
  185. 176 }
  186. 177
  187. 178 int SlidePage::getPageCount()
  188. 179 {
  189. 180     return pageCount;
  190. 181 }
  191. 182
  192. 183 int SlidePage::getCurrentPageIndex()
  193. 184 {
  194. 185     return pageIndex;
  195. 186 }
  196. 187
  197. 188 void SlidePage::onCurrentPageIndexChanged(int index)
  198. 189 {
  199. 190     for (int i = 0; i < pageIndicator.count(); i++) {
  200. 191         if (i == index)
  201. 192             pageIndicator->setPixmap(QPixmap(":/icons/indicator2.png"));
  202. 193         else
  203. <span style="font-style: italic;"><span style="font-style: normal;">194             pageIndicator->setPixmap(QPixmap(":/icons/indicator1.png"));
  204. 195     }
  205. 196 }</span></span></font>
复制代码

        可以看到主程序代码量不多,仅200行不到就可以完成这样的滑动页面设计。(上面代码单行较长,由于Word排版,会换行,若影响阅读,请打开源项目查看)。比网上用QPainter与QMouseMove实现页面滑动省了很多代码。
        我们在第七章学习过了QScrollArea,这是一个滑动的界面,初学时我们只知道它只能通过两边的滑块来滚动界面。实际上,查Qt帮助文档资料得知,Qt有一个QScroller类就可以实现滑动界面。它能控制那种带滚动条的类的界面的滑动。详细可以看源码39行至41行。
        滑动界面部分是由QScroller类控制。同时通过设置QScrollArea-> horizontalScrollBar()这个滚动条的value值就可以控制界面所处位置了。原理看似简单,详细需要查看程序,请读者带着程序的原理通过细读程序源码来分析。
        注意!运行这个项目时,您会发现这是一个空白的窗口!因为这只是一个滑动页面的类,我们还没有在里面添加内容!所以接着往下看,我们先开发一页APP界面。
        运行项目效果如下。因为我们还没有给滑动页面类加内容。所以是无法滑动的。继续往下看。
APP主界面开发项目8004.png

26.2 APP界面开发
       本小节实现了一个车载音乐界面的界面开发。注意只是界面开发,不实现其功能。读者可以参考来实现自己的界面。开发界面除了自己的想法,还需要有美工基础,我们前面已经学习过布局了,APP界面,主要都是一些布局的设计。这里就不贴代码了。(代码都是一些布局设置,与按钮排布,读者有前面的基础后,自行查阅项目源码)。
项目路径为4/03_appmainview/appdemo/appdemo.pro。项目运行效果如下。
APP主界面开发项目8222.png

        项目运行后,可以看到这样的一页这样的APP主界面,注意不能点击,这只是界面而已!读者就可以仿照这样的一个APP界面来开发自己的应用界面了。开发时再通过链接到界面上的按钮的点击信号,比如点击按钮后打开新的页面,这样就可以完成一个完整的点击交互事件了!
26.3 APP主界面项目综合测试
项目路径为4/03_appmainview/03_appmainview /03_appmainview.pro。打开此项目您将看到如下。
APP主界面开发项目8438.png

项目文件夹下内容解释:
        03_appmainview.pro项目下:
appdemo文件夹为车载音乐APP页面,只是界面,不带实际功能!
slidepage文件夹为编者原创的一个滑动页面类,在这个类里,我们可以使用addPage()方法来添加页面,当添加的页面大于2页时,就可以滑动切换页面了。
Headers文件夹为03_appmainview.pro的头文件。
Sources文件夹为03_appmainview.pro的源文件。
Resource文件夹为03_appmainview.pro的源文件。主要是存放一张背景图片。
源程序路径为4/03_appmainview/03_appmainview /widget.cpp。源码如下。
  1. <font size="2">   /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   03_appmainview
  4.     * @brief         widget.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank" style="">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank" style="">www.openedv.com</a>
  8.     * @date           2021-06-09
  9.     *******************************************************************/
  10. 1   #include "widget.h"
  11. 2   #include <QPushButton>
  12. 3   #include <QDebug>
  13. 4
  14. 5   AppMainView::AppMainView(QWidget *parent)
  15. 6   {
  16. 7       this->setParent(parent);
  17. 8       this->setGeometry(0, 0, 800, 480);
  18. 9       this->setMinimumSize(800, 480);
  19. 10
  20. 11      bgWidget = new QWidget(this);
  21. 12      bgWidget->setStyleSheet("border-image: url(:/images/bg.png)");
  22. 13
  23. 14      mySlidePage = new SlidePage(this);
  24. 15      mySlidePage->resize(this->size());
  25. 16
  26. 17      for (int i = 0; i < 3; i++) {
  27. 18          appDemo<span style="font-style: italic;"><span style="font-style: normal;"> = new AppDemo();
  28. 19          mySlidePage->addPage(appDemo</span><span style="font-style: normal;">);
  29. 20      }
  30. 21
  31. 22  }
  32. 23
  33. 24  AppMainView::~AppMainView()
  34. 25  {
  35. 26  }
  36. 27
  37. 28
  38. 29  void AppMainView::resizeEvent(QResizeEvent *event)
  39. 30  {
  40. 31      Q_UNUSED(event)
  41. 32      mySlidePage->resize(this->size());
  42. 33      bgWidget->resize(this->size());
  43. 34  }</span></span></font>
复制代码

        可以看到01_appmainview.pro项目里的内容较少,因为01_appmainview.pro这个项目是调用了前面两节的项目,所以在这个项目里看到的内容较少。
        第14行,调用了26.1小节里的滑动界面类,实例化了一个对象mySlidePage。我们只需要往这个对象了添加Widget类型对象,就相当于为这个滑动界面类添加了对象!使用编者封装好的一个 addPage(QWidget *)就可以轻松的添加页面了。
        17~20行,APP页面,这里是一个AppDemo类,然后实例化了3个对象,也就是说会有3个APP页面,而且他们都是一样的。第19行,如编者所说,使用滑动界面对象mySlidePage使用方法addPage(),将3个APP页面添加到滑动界面对象里,然后就可以左右滑动这个APP主界面了。
26.3.1 程序运行效果
        程序运行效果如下。可以看到有3个页面,而且他们的样子都是一样的,这是编者为了方便测试滑动页面的效果而添加这三个页面。在界面的下方,如果有细心的读者发现,这里还有三个分页逗点,可以说明这是三个页面。读者可以运行程序来体验滑动效果及界面设计的效果。可以交叉编译到开发板上运行,无论是界面设计感还是流畅度都很不错。
APP主界面开发项目10535.png

回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 02:27

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表