Qt重寫QStackedWidget模擬實現(xiàn)home界面滑動效果
在上章我們學(xué)習(xí)了QScroller實現(xiàn)home界面滑動效果,但是該界面是實現(xiàn)的上下滑動效果,如果想模擬手機home滑動界面,則需要實現(xiàn)左右滑動效果.
本章,則重寫QStackedWidget類,來真正的模擬手機,來實現(xiàn)home界面左右滑動效果.
1.SmoothStackedWidget類實現(xiàn)
demo界面如下圖所示(創(chuàng)建了4個子界面):

(支持快滑,慢滑):

如果是慢滑,則根據(jù)當(dāng)前滑到的界面處于哪一頁占比更多,則就跳到哪里.
否則就是快滑,根據(jù)滑動的偏移值來決定跳轉(zhuǎn)
同樣也支持邊緣滑動檢測(已在最邊緣時,則滑動速率減慢,告訴用戶已到邊緣):

2.代碼實現(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 //平滑滑動時的最大延遲時間
#define SMOOTH_EDGE_MOVE_RATIO 0.14 //邊緣移動系數(shù),范圍0~1,越低越慢
typedef enum tagScrollMouseDragInfo {
MOUSE_RELEASE = 0, //鼠標(biāo)離開
MOUSE_PRESS = 1, //按下
MOUSE_PRESS_MOVE = 2, //按下移動
MOUSE_RELEASE_MOVE = 3 //鼠標(biāo)離開并滑動
}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)(手指)按下移動時,則調(diào)用SmoothMove(offset),通過offset來動態(tài)顯示滑動的界面.
當(dāng)鼠標(biāo)(手指)松開后,則調(diào)用SmoothAnimationStart()來實現(xiàn)界面移動(到底是切換上一頁、還是切換下一頁、還是當(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; //拖動時的坐標(biāo)
static qint64 pressMSec ;
if(mouse && w && animation->state() == QAbstractAnimation::Stopped)
{
if( mouse->type() ==QEvent::MouseButtonPress) //首次按下
{
pressMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //記錄按下的時間
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) //未移動
{
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) //滑動結(jié)束,啟動平滑滑動
{
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; //計算每秒像素點
m_switchFlag = SWITCH_NONE;
int moveX = qAbs(m_smoothWidget.x());
float temp = width()*0.5;
int animationEndX;
//慢速滑動(速度過慢||時間過長),則根據(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 { // 否則就是快速滑動
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;
}
}
}
//根據(jù)每秒滑動像素點,來計算滑動時長.
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();
}到此這篇關(guān)于Qt重寫QStackedWidget模擬實現(xiàn)home界面滑動效果的文章就介紹到這了,更多相關(guān)Qt QStackedWidget界面滑動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++文件關(guān)鍵詞快速定位出現(xiàn)的行號實現(xiàn)高效搜索
這篇文章主要為大家介紹了C++文件關(guān)鍵詞快速定位出現(xiàn)的行號實現(xiàn)高效搜索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
利用C++實現(xiàn)矩陣的相加/相稱/轉(zhuǎn)置/求鞍點
利用C++實現(xiàn)矩陣的相加/相稱/轉(zhuǎn)置/求鞍點。需要的朋友可以過來參考下,希望對大家有所幫助2013-10-10
C/C++ Socket設(shè)置接收超時時間的多種方法
網(wǎng)絡(luò)編程中經(jīng)常需要處理的一個問題就是如何正確地處理Socket超時,對于C/C++,有幾種常用的技術(shù)可以用來設(shè)置Socket接收超時時間,在這篇文章中,我們將詳細介紹如何在C/C++中設(shè)置Socket的非阻塞模式以及如何配置接收超時時間,需要的朋友可以參考下2024-01-01

