在上章我们学习了QScroller实现home界面滑动效果,但是该界面是实现的上下滑动效果,如果想模拟手机home滑动界面,则需要实现左右滑动效果.
本章,则重写QStackedWidget类,来真正的模拟手机,来实现home界面左右滑动效果.
1.SmoothStackedWidget类实现
demo界面如下图所示(创建了4个子界面):
(支持快滑,慢滑)
如果是慢滑,则根据当前滑到的界面处于哪一页占比更多,则就跳到哪里.
否则就是快滑,根据滑动的偏移值来决定跳转
同样也支持边缘滑动检测(已在最边缘时,则滑动速率减慢,告诉用户已到边缘)
2.代码实现
头文件如下所示:
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
- #ifndef SMOOTHSTACKEDWIDGET_H
- #define SMOOTHSTACKEDWIDGET_H
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- class SmoothStackedWidget : public QStackedWidget
- {
- Q_OBJECT
-
- #define SMOOTH_MAX_MS 900 //平滑滑动时的最大延迟时间
- #define SMOOTH_EDGE_MOVE_RATIO 0.14 //边缘移动系数,范围0~1,越低越慢
-
- typedef enum tagScrollMouseDragInfo {
- MOUSE_RELEASE = 0, //鼠标离开
- MOUSE_PRESS = 1, //按下
- MOUSE_PRESS_MOVE = 2, //按下移动
- MOUSE_RELEASE_MOVE = 3 //鼠标离开并滑动
- }Scroll_Mouse_Drag_INFO_E;
-
- typedef enum tagSmoothAnimationSwitchInfo {
- SWITCH_PRE = -1, //切换上一页
- SWITCH_NONE = 0, //不切换
- SWITCH_NEXT = 1, //切换下一页
- }AnimationSwitch_Drag_INFO_E;
-
- Scroll_Mouse_Drag_INFO_E m_dragFlag = MOUSE_RELEASE;
- AnimationSwitch_Drag_INFO_E m_switchFlag = SWITCH_NONE;
-
- QWidget *m_parent;
-
- QWidget m_smoothWidget;
-
- int m_smoothCurrentIndex=-1;
-
- QPropertyAnimation *animation;
-
- float m_smoothMovePos;
-
- bool eventFilter(QObject *obj, QEvent *evt) override;
-
- void paintEvent(QPaintEvent *event) override;
-
- void resizeEvent(QResizeEvent *event) override;
-
- void SmoothLoadPixmap(bool isSmoothUpdate = false);
- void SmoothStartMove();
-
- void SmoothMove(int offset);
-
- void SmoothAnimationStart(int startPos, int endPos, int durationMs);
-
- void SmoothAnimationInit();
-
- public:
- explicit SmoothStackedWidget(QWidget *parent = nullptr);
-
- int addWidget(QAbstractScrollArea *w);
-
- int addWidget(QWidget *w);
-
- void setCurrentIndex(int index);
-
- void removeWidget(QWidget *w);
-
- void IconUpdate(); //刷新页数标签
-
- void UpdateSmooth();
-
- signals:
-
- protected slots:
- void OnSmoothAnimationFinished();
-
- };
-
- #endif // SMOOTHSTACKEDWIDGET_H
其中eventFilter()函数如下所示:
当鼠标(手指)按下移动时,则调用SmoothMove(offset),通过offset来动态显示滑动的界面.
当鼠标(手指)松开后,则调用SmoothAnimationStart()来实现界面移动(到底是切换上一页、还是切换下一页、还是当前页).
- bool SmoothStackedWidget::eventFilter(QObject *obj, QEvent *evt)
- {
- QMouseEvent *mouse = dynamic_cast<QMouseEvent *>(evt);
- QWidget *w = dynamic_cast<QWidget *>(obj);
-
- static int pressPoint_x = 0; //按下的坐标
- static int dragPoint_x = -1; //拖动时的坐标
- static qint64 pressMSec ;
-
- if(mouse && w && animation->state() == QAbstractAnimation::Stopped)
- {
- if( mouse->type() ==QEvent::MouseButtonPress) //首次按下
- {
- pressMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //记录按下的时间
- dragPoint_x = mouse->pos().x(); //当前坐标
- pressPoint_x = dragPoint_x; //按下的位置
- m_dragFlag = MOUSE_PRESS;
- }
- else if(mouse->type() == QEvent::MouseButtonRelease &&
- m_dragFlag == MOUSE_PRESS) //未移动
- {
- m_dragFlag = MOUSE_RELEASE;
- }
- else if(mouse->type() == QEvent::MouseMove &&
- m_dragFlag == MOUSE_PRESS) //初次滑动,判断移动阀值,避免误操作
- {
- if(qAbs(dragPoint_x - mouse->pos().x()) > 3) //判断移动阀值,避免误操作
- {
- dragPoint_x = mouse->pos().x();
- SmoothStartMove();
- m_dragFlag = MOUSE_PRESS_MOVE;
- }
- }
- else if(mouse->type() == QEvent::MouseMove &&
- m_dragFlag== MOUSE_PRESS_MOVE ) //正在滑动
- {
- int offset = ( mouse->pos().x() - dragPoint_x);
- SmoothMove(offset);
- dragPoint_x = mouse->pos().x();
- }
- else if(mouse->type() == QEvent::MouseButtonRelease &&
- m_dragFlag == MOUSE_PRESS_MOVE) //滑动结束,启动平滑滑动
- {
- int durationMs= QDateTime::currentDateTime().toMSecsSinceEpoch()-pressMSec;
- SmoothAnimationStart(pressPoint_x,mouse->pos().x(),durationMs);
- m_dragFlag = MOUSE_RELEASE;
- }
- }
-
- return QWidget::eventFilter(obj,evt);
- }
SmoothAnimationStart()函数如下所示:
- void SmoothStackedWidget::SmoothAnimationStart(int startPos, int endPos, int durationMs)
- {
- int pixelPerSecond=qAbs(endPos - startPos)*1000/durationMs; //计算每秒像素点
- m_switchFlag = SWITCH_NONE;
- int moveX = qAbs(m_smoothWidget.x());
- float temp = width()*0.5;
- int animationEndX;
-
- //慢速滑动(速度过慢||时间过长),则根据当前滑到哪里,就跳到哪里
- if(pixelPerSecond<300 || durationMs > 1000) {
- if(moveX < (temp)) { //[0,width/2] = 上一页
- if(currentIndex()==0) {
- animationEndX = -width();
- } else {
- animationEndX = 0;
- m_switchFlag = SWITCH_PRE;
- }
- } else if(moveX < (temp*3)) { //[width/2,width*3/2] = 当前一页
- animationEndX = -width();
- } else {
- if(currentIndex()==(count()-1)) { //[width*3/2,width*2] = 下一页
- animationEndX = -width();
- } else {
- animationEndX = -width()*2;
- m_switchFlag = SWITCH_NEXT;
- }
- }
-
- } else { // 否则就是快速滑动
- if(startPos < endPos) { //向右滑动
- if(currentIndex()==0) {
- animationEndX = -width();
- } else {
- animationEndX = 0;
- m_switchFlag = SWITCH_PRE;
- }
- } else { //向左滑动
- if(currentIndex()==(count()-1)) {
- animationEndX = -width();
- } else {
- animationEndX = -width()*2;
- m_switchFlag = SWITCH_NEXT;
- }
- }
- }
-
- //根据每秒滑动像素点,来计算滑动时长.
- int animationDuration = durationMs;
- float xOffsetRatio = qAbs(animationEndX - m_smoothWidget.x()) / (static_cast<float>(width())); //计算滑动占整屏比例
-
- if(animationDuration > (SMOOTH_MAX_MS * xOffsetRatio)) //滑动时间过大,则重置
- animationDuration = SMOOTH_MAX_MS * xOffsetRatio;
-
-
- animation->setDuration(animationDuration);
- animation->setStartValue(m_smoothWidget.geometry());
- animation->setEndValue(QRect(animationEndX, m_smoothWidget.y(), m_smoothWidget.width(), m_smoothWidget.height()));
- animation->start();
- }
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓