前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >63.QT-重写QStackedWidget实现home界面左右滑动

63.QT-重写QStackedWidget实现home界面左右滑动

作者头像
诺谦
发布2021-01-13 11:25:30
1.9K0
发布2021-01-13 11:25:30
举报
文章被收录于专栏:Linux驱动Linux驱动

在上章我们学习了62.QT-QScroller实现home界面滑动效果,但是该界面是实现的上下滑动效果,如果想模拟手机home滑动界面,则需要实现左右滑动效果.

本章,则重写QStackedWidget类,来实现模拟手机home界面左右滑动效果.

1.SmoothStackedWidget类实现

demo界面如下图所示(创建了4个子界面):

效果图如下所示(支持快滑,慢滑):

如果是慢滑,则根据当前滑到的界面处于哪一页占比更多,则就跳到哪里.

同样也支持边缘滑动检测(已在最边缘时,则滑动速率减慢,告诉用户已到边缘):

2.代码实现

头文件如下所示:

代码语言:javascript
复制
#ifndef SMOOTHSTACKEDWIDGET_H
#define SMOOTHSTACKEDWIDGET_H

#include <QObject>
#include <QWidget>
#include <QStackedWidget>
#include <QAbstractScrollArea>
#include <QPixmap>
#include <QPropertyAnimation>

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()来显示滑动界面.

当鼠标(手指)松开后,则调用SmoothAnimationStart()来实现界面移动(到底是切换上一页、还是切换下一页、还是当前页).

代码语言:javascript
复制
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()函数如下所示:

代码语言:javascript
复制
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();
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-01-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档