Qt重寫QStackedWidget模擬實(shí)現(xiàn)home界面滑動(dòng)效果
在上章我們學(xué)習(xí)了QScroller實(shí)現(xiàn)home界面滑動(dòng)效果,但是該界面是實(shí)現(xiàn)的上下滑動(dòng)效果,如果想模擬手機(jī)home滑動(dòng)界面,則需要實(shí)現(xiàn)左右滑動(dòng)效果.
本章,則重寫QStackedWidget類,來真正的模擬手機(jī),來實(shí)現(xiàn)home界面左右滑動(dòng)效果.
1.SmoothStackedWidget類實(shí)現(xiàn)
demo界面如下圖所示(創(chuàng)建了4個(gè)子界面):
(支持快滑,慢滑):
如果是慢滑,則根據(jù)當(dāng)前滑到的界面處于哪一頁占比更多,則就跳到哪里.
否則就是快滑,根據(jù)滑動(dòng)的偏移值來決定跳轉(zhuǎn)
同樣也支持邊緣滑動(dòng)檢測(已在最邊緣時(shí),則滑動(dòng)速率減慢,告訴用戶已到邊緣):
2.代碼實(shí)現(xiàn)
頭文件如下所示:
#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 //平滑滑動(dòng)時(shí)的最大延遲時(shí)間 #define SMOOTH_EDGE_MOVE_RATIO 0.14 //邊緣移動(dòng)系數(shù),范圍0~1,越低越慢 typedef enum tagScrollMouseDragInfo { MOUSE_RELEASE = 0, //鼠標(biāo)離開 MOUSE_PRESS = 1, //按下 MOUSE_PRESS_MOVE = 2, //按下移動(dòng) MOUSE_RELEASE_MOVE = 3 //鼠標(biāo)離開并滑動(dòng) }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(); //刷新頁數(shù)標(biāo)簽 void UpdateSmooth(); signals: protected slots: void OnSmoothAnimationFinished(); }; #endif // SMOOTHSTACKEDWIDGET_H
其中eventFilter()函數(shù)如下所示:
當(dāng)鼠標(biāo)(手指)按下移動(dòng)時(shí),則調(diào)用SmoothMove(offset),通過offset來動(dòng)態(tài)顯示滑動(dòng)的界面.
當(dāng)鼠標(biāo)(手指)松開后,則調(diào)用SmoothAnimationStart()來實(shí)現(xiàn)界面移動(dòng)(到底是切換上一頁、還是切換下一頁、還是當(dāng)前頁).
bool SmoothStackedWidget::eventFilter(QObject *obj, QEvent *evt) { QMouseEvent *mouse = dynamic_cast<QMouseEvent *>(evt); QWidget *w = dynamic_cast<QWidget *>(obj); static int pressPoint_x = 0; //按下的坐標(biāo) static int dragPoint_x = -1; //拖動(dòng)時(shí)的坐標(biāo) static qint64 pressMSec ; if(mouse && w && animation->state() == QAbstractAnimation::Stopped) { if( mouse->type() ==QEvent::MouseButtonPress) //首次按下 { pressMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //記錄按下的時(shí)間 dragPoint_x = mouse->pos().x(); //當(dāng)前坐標(biāo) pressPoint_x = dragPoint_x; //按下的位置 m_dragFlag = MOUSE_PRESS; } else if(mouse->type() == QEvent::MouseButtonRelease && m_dragFlag == MOUSE_PRESS) //未移動(dòng) { m_dragFlag = MOUSE_RELEASE; } else if(mouse->type() == QEvent::MouseMove && m_dragFlag == MOUSE_PRESS) //初次滑動(dòng),判斷移動(dòng)閥值,避免誤操作 { if(qAbs(dragPoint_x - mouse->pos().x()) > 3) //判斷移動(dòng)閥值,避免誤操作 { dragPoint_x = mouse->pos().x(); SmoothStartMove(); m_dragFlag = MOUSE_PRESS_MOVE; } } else if(mouse->type() == QEvent::MouseMove && m_dragFlag== MOUSE_PRESS_MOVE ) //正在滑動(dòng) { 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) //滑動(dòng)結(jié)束,啟動(dòng)平滑滑動(dòng) { int durationMs= QDateTime::currentDateTime().toMSecsSinceEpoch()-pressMSec; SmoothAnimationStart(pressPoint_x,mouse->pos().x(),durationMs); m_dragFlag = MOUSE_RELEASE; } } return QWidget::eventFilter(obj,evt); }
SmoothAnimationStart()函數(shù)如下所示:
void SmoothStackedWidget::SmoothAnimationStart(int startPos, int endPos, int durationMs) { int pixelPerSecond=qAbs(endPos - startPos)*1000/durationMs; //計(jì)算每秒像素點(diǎn) m_switchFlag = SWITCH_NONE; int moveX = qAbs(m_smoothWidget.x()); float temp = width()*0.5; int animationEndX; //慢速滑動(dòng)(速度過慢||時(shí)間過長),則根據(jù)當(dāng)前滑到哪里,就跳到哪里 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] = 當(dāng)前一頁 animationEndX = -width(); } else { if(currentIndex()==(count()-1)) { //[width*3/2,width*2] = 下一頁 animationEndX = -width(); } else { animationEndX = -width()*2; m_switchFlag = SWITCH_NEXT; } } } else { // 否則就是快速滑動(dòng) if(startPos < endPos) { //向右滑動(dòng) if(currentIndex()==0) { animationEndX = -width(); } else { animationEndX = 0; m_switchFlag = SWITCH_PRE; } } else { //向左滑動(dòng) if(currentIndex()==(count()-1)) { animationEndX = -width(); } else { animationEndX = -width()*2; m_switchFlag = SWITCH_NEXT; } } } //根據(jù)每秒滑動(dòng)像素點(diǎn),來計(jì)算滑動(dòng)時(shí)長. int animationDuration = durationMs; float xOffsetRatio = qAbs(animationEndX - m_smoothWidget.x()) / (static_cast<float>(width())); //計(jì)算滑動(dòng)占整屏比例 if(animationDuration > (SMOOTH_MAX_MS * xOffsetRatio)) //滑動(dòng)時(shí)間過大,則重置 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(); }
到此這篇關(guān)于Qt重寫QStackedWidget模擬實(shí)現(xiàn)home界面滑動(dòng)效果的文章就介紹到這了,更多相關(guān)Qt QStackedWidget界面滑動(dòng)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言庫函數(shù)qsort及bsearch快速排序算法使用解析
這篇文章主要為大家介紹了C語言庫函數(shù)qsort及bsearch快速排序算法的使用示例解析2022-02-02FFmpeg實(shí)現(xiàn)音頻漸響效果參數(shù)值詳解
這篇文章主要為大家介紹了FFmpeg實(shí)現(xiàn)音頻漸響效果參數(shù)值詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10C++超詳細(xì)分析單鏈表的實(shí)現(xiàn)與常見接口
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的,本章帶你分析單鏈表的實(shí)現(xiàn)與常見接口2022-03-03