• 【正点原子Linux连载】第二十六章 APP主界面开发项目 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2


    1)实验平台:正点原子阿尔法Linux开发板
    2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
    3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
    4)对正点原子Linux感兴趣的同学可以加群讨论:935446741

    第二十六章 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。

        /******************************************************************
        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 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205

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

    我们在第七章学习过了QScrollArea,这是一个滑动的界面,初学时我们只知道它只能通过两边的滑块来滚动界面。实际上,查Qt帮助文档资料得知,Qt有一个QScroller类就可以实现滑动界面。它能控制那种带滚动条的类的界面的滑动。详细可以看源码39行至41行。

    滑动界面部分是由QScroller类控制。同时通过设置QScrollArea-> horizontalScrollBar()这个滚动条的value值就可以控制界面所处位置了。原理看似简单,详细需要查看程序,请读者带着程序的原理通过细读程序源码来分析。

    注意!运行这个项目时,您会发现这是一个空白的窗口!因为这只是一个滑动页面的类,我们还没有在里面添加内容!所以接着往下看,我们先开发一页APP界面。

    运行项目效果如下。因为我们还没有给滑动页面类加内容。所以是无法滑动的。继续往下看。
    在这里插入图片描述

    26.2 APP界面开发

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

    项目运行后,可以看到这样的一页这样的APP主界面,注意不能点击,这只是界面而已!读者就可以仿照这样的一个APP界面来开发自己的应用界面了。开发时再通过链接到界面上的按钮的点击信号,比如点击按钮后打开新的页面,这样就可以完成一个完整的点击交互事件了!
    
    • 1

    26.3 APP主界面项目综合测试

    项目路径为4/03_appmainview/03_appmainview /03_appmainview.pro。打开此项目您将看到如下。
    在这里插入图片描述

    项目文件夹下内容解释:
    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。源码如下。

        /******************************************************************
        Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
        * @projectName   03_appmainview
        * @brief         widget.cpp
        * @author        Deng Zhimao
        * @email         1252699831@qq.com
        * @net            www.openedv.com
        * @date           2021-06-09
        *******************************************************************/
    1   #include "widget.h"
    2   #include <QPushButton>
    3   #include <QDebug>
    4 
    5   AppMainView::AppMainView(QWidget *parent)
    6   {
    7       this->setParent(parent);
    8       this->setGeometry(0, 0, 800, 480);
    9       this->setMinimumSize(800, 480);
    10
    11      bgWidget = new QWidget(this);
    12      bgWidget->setStyleSheet("border-image: url(:/images/bg.png)");
    13
    14      mySlidePage = new SlidePage(this);
    15      mySlidePage->resize(this->size());
    16
    17      for (int i = 0; i < 3; i++) {
    18          appDemo[i] = new AppDemo();
    19          mySlidePage->addPage(appDemo[i]);
    20      }
    21
    22  }
    23
    24  AppMainView::~AppMainView()
    25  {
    26  }
    27
    28
    29  void AppMainView::resizeEvent(QResizeEvent *event)
    30  {
    31      Q_UNUSED(event)
    32      mySlidePage->resize(this->size());
    33      bgWidget->resize(this->size());
    34  }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    可以看到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个页面,而且他们的样子都是一样的,这是编者为了方便测试滑动页面的效果而添加这三个页面。在界面的下方,如果有细心的读者发现,这里还有三个分页逗点,可以说明这是三个页面。读者可以运行程序来体验滑动效果及界面设计的效果。可以交叉编译到开发板上运行,无论是界面设计感还是流畅度都很不错。
    在这里插入图片描述

  • 相关阅读:
    Redis~01 缓存:如何利用读缓存提高系统性能?
    【数据结构初阶】--- 栈和队列
    R语言survminer包的ggsurvplot函数可视化生存曲线、legend.title指定图例的标题、legend.labs指定图例标签文本
    Ubuntu设置开机执行sh脚本
    leetcode:801. 使序列递增的最小交换次数【线性dp + 考虑当前和前一位 + java】
    【Try to Hack】Windows系统账户安全
    lazarus开发应用提供http接口
    Unity UI制作
    0068【Edabit ★☆☆☆☆☆】I‘d Like a New Shade of Blue, Please
    疫苗预约小程序,疫苗预约微信小程序,疫苗接种小程序毕业设计作品
  • 原文地址:https://blog.csdn.net/weixin_55796564/article/details/125892048