zoukankan      html  css  js  c++  java
  • 63.QT-重写QStackedWidget模仿iphone的home界面,实现左右滑动

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

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

    1.SmoothStackedWidget类实现

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

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

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

    否则就是快滑,根据滑动的偏移值来决定跳转

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

    demo下载:https://download.csdn.net/download/qq_37997682/14109512

    2.代码实现

    头文件如下所示:

    #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(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();
    }
  • 相关阅读:
    043_生成随机密码
    042_提示用户输入年份后测试判断是否为闰年
    041_查找 Linux 系统中的僵尸进程
    040_删除某个目录下大小为 0 的文件
    039_显示 CPU 厂商信息
    038_使用脚本自动创建逻辑卷
    037_自动添加防火墙规则,开启某些服务或端口(适用于 RHEL7)
    bzoj1537
    bzoj2466
    bzoj1047
  • 原文地址:https://www.cnblogs.com/lifexy/p/14260022.html
Copyright © 2011-2022 走看看