一、觸控時代的滾動工具:QScroller類設計介紹
1.1 從機械滾輪到數字慣性
在觸控設備普及前,滾動操作如同老式打字機的滾軸,只能通過鼠標滾輪或滾動條進行離散式控制。QScroller的出現如同給數字界面裝上了"慣性飛輪",通過模擬物理上的動量守恒定律,讓滾動操作具備以下特性:
- 速度傳遞:滑動速度決定滾動距離;
- 動態衰減:摩擦系數影響停止時間;
- 邊界彈性:類似橡皮筋的越界回彈效果;
1.2 類關系拓撲圖
QScroller ────┬── QScrollerProperties ├── QScrollEvent └── QScrollPrepareEvent
QScroller作為控制器,通過QScrollerProperties配置物理參數,生成QScrollEvent事件以驅動界面的刷新。
二、核心類深度解析
2.1 QScroller類:滾動引擎
關鍵方法說明:
// 啟用控件手勢支持(核心入口)
static QScroller *QScroller::grabGesture(QObject *target, QScroller::GestureType gestureType = TouchGesture
);//檢查指定的目標對象是否已經關聯了 QScroller 實例
static bool hasScroller(QObject *target) ;//使指定的目標對象停止捕獲指定類型的滾動手勢
static void ungrabGesture(QObject *target,
QScroller::ScrollerGestureType gestureType);// 獲取當前滾動狀態
QScroller::State state() const; // 滾動到指定區域(帶動畫)
void scrollTo(const QVector2D &pos, qreal scrollTime = 0);// 確保子控件可見(自動計算滾動路徑)
void ensureVisible(const QRectF &rect, qreal xmargin = 0.1, qreal ymargin = 0.1);//設置滾動器的屬性,如滾動速度、摩擦力、彈性等
void setScrollerProperties(const QScrollerProperties &properties);//啟動滾動器,開始滾動操作
void start();//停止滾動器,終止滾動操作
void stop();//設置水平方向的捕捉位置。當滾動到這些位置時,滾動器會自動對齊到這些位置。
void setSnapPositionsX(const QList<qreal> &positions);//設置垂直方向的捕捉位置。當滾動到這些位置時,滾動器會自動對齊到這些位置。
void setSnapPositionsY(const QList<qreal> &positions);
手勢類型枚舉:
enum GestureType {TouchGesture, // 觸摸手勢 LeftMouseButtonGesture, // 左鍵拖拽RightMouseButtonGesture // 右鍵拖拽(特殊場景)
};
2.2 QScrollerProperties:設置和調整配置參數
這個類就像汽車的"懸掛調校系統"調節汽車引擎一樣,專門調整設置QScroller類的配置參數,包含幾十個可配置參數,主要有:
QScrollerProperties properties;//設置滾輪滾動過程中畫面的幀率,幀率越高看著越舒服,幀率越低畫面越跳動
properties.setScrollMetric(QScrollerProperties::FrameRate,QScrollerProperties::Fps30);
//設置平滑速度,當滑動完手離開屏幕后,進行平滑滑動的速度,此值應介于0和1之間。值越小,速度越慢。但我試過實際沒太明顯的區別
properties.setScrollMetric(QScrollerProperties::DragVelocitySmoothingFactor,0.3);
//設置鼠標釋放后滾動到停止時的運動曲線,參數為QEasingCurve類型,不能設置為QEasingCurve::Type類型,不會隱式轉換
properties.setScrollMetric(QScrollerProperties::ScrollingCurve,QEasingCurve::OutQuad);
//設置移動閥值(按下后需要移動最少距離后,觸發滑動),用來避免誤操作
properties.setScrollMetric(QScrollerProperties::DragStartDistance,0.003);
//設置減速因子,值越大,減速越快,進而會影響點擊釋放后滾動的距離。對于大多數類型,該值應在0.1到2.0的范圍內
properties.setScrollMetric(QScrollerProperties::DecelerationFactor,0.4);
//設置當運動方向與某一個軸的角度小于該設定值(0~1)時,則限定只有該軸方向的滾動,一般就用0.5,兩個軸都是,不然要么區域重疊,要么區域漏掉
properties.setScrollMetric(QScrollerProperties::AxisLockThreshold,0.5);
//自動滾動過程中,鼠標點擊操作會停止當前滾動,當速度大于該設定(m/s)時,鼠標事件不會傳遞給目標即不會停止滾動
properties.setScrollMetric(QScrollerProperties::MaximumClickThroughVelocity,0.5);
//與AcceleratingFlickSpeedupFactor配合使用。設置時間及加速因子
properties.setScrollMetric(QScrollerProperties::AcceleratingFlickMaximumTime ,3);
//與AcceleratingFlickMaximumTime配合使用,應>=1
properties.setScrollMetric(QScrollerProperties::AcceleratingFlickSpeedupFactor,2);
//設置自動滑動的最小速度,m/s,如果手勢的速度小于該速度,則不會觸發自動滾動,所以可以設置得小一些,防止手指輕微移動引起屏幕滾動
properties.setScrollMetric(QScrollerProperties::MinimumVelocity,0.2);
//設置自動滾動能達到得最大速度,m/s
properties.setScrollMetric(QScrollerProperties::MaximumVelocity,0.2);
//設置當拖拽過量時,釋放后位置恢復所用時間(s)
properties.setScrollMetric(QScrollerProperties::OvershootScrollTime,0.2);
//設置滾動曲線的時間因子。設置滾動的時長,值越小,滾動時間越長
properties.setScrollMetric(QScrollerProperties::SnapTime,0.2);
//設置過量拖拽的距離占頁面的比例,0~1,比如設置0.2,過量拖拽垂直最多移動高度的1/5
properties.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor,0.2);
//設置垂直向允許過量拖拽的策略,可以設置滾動條出現時開啟、始終關閉、始終開啟三種策略
properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,QScrollerProperties::OvershootWhenScrollable);
//設置過量拖拽的移動距離與鼠標移動距離的比例,0~1,值越小表現出的阻塞感越強
properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor,0.2);
//設置自動滾動時允許的過量滾動距離比例
properties.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor,0.2);
//設置鼠標事件延遲時間,單位s。
properties.setScrollMetric(QScrollerProperties::MousePressEventDelay,1);QScroller::scroller(ui->scrollArea)->setScrollerProperties(properties);
三、實現原理揭秘
3.1 事件處理流水線
sequenceDiagram participant User participant QScroller participant Widget User->>QScroller: 觸摸/鼠標按下 QScroller->>Widget: 發送ScrollPrepare事件 Widget->>QScroller: 返回內容尺寸等信息 loop 每幀更新 QScroller->>QScroller: 計算物理運動參數 QScroller->>Widget: 發送ScrollEvent Widget->>Widget: 更新顯示位置 end User->>QScroller: 釋放操作 QScroller->>Widget: 觸發慣性滾動
3.2 物理模型公式
滾動位置計算采用改進型牛頓運動方程:
當前位置 = 初始位置 + 初速度 * t - 0.5 * 摩擦系數 * t2
其中摩擦系數通過QScrollerProperties::DecelerationFactor動態調整
四、實用代碼示例
4.1 基礎滑動實現
// 為QListWidget啟用觸控滑動
QListWidget *list = new QListWidget(this);
QScroller *scroller = QScroller::scroller(list->viewport());
QScroller::grabGesture(list->viewport(), QScroller::TouchGesture);// 配置滑動參數(類似手機APP的流暢感)
QScrollerProperties prop = scroller->scrollerProperties();
prop.setScrollMetric(QScrollerProperties::DragVelocitySmoothingFactor, 0.6);
prop.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.3);
scroller->setScrollerProperties(prop);
4.2 高級分頁滾動
// 相冊瀏覽分頁效果
QScrollArea *gallery = new QScrollArea(this);
QScroller::grabGesture(gallery, QScroller::LeftMouseButtonGesture);QScrollerProperties prop;
prop.setScrollMetric(QScrollerProperties::SnapPositionRatio, 1.0); // 整頁吸附
prop.setScrollMetric(QScrollerProperties::SnapTime, 0.5); // 過渡動畫時長
QScroller::scroller(gallery)->setScrollerProperties(prop);
五、性能優化指南
- 渲染優化:
// 啟用像素級滾動(避免跳躍感)
listWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- 內存管理:
// 及時釋放不再使用的滾動器
QScroller::ungrabGesture(scrollArea); // 類似斷開剎車系統
- 動態調參技巧:
// 根據設備類型切換參數
#ifdef Q_OS_ANDROID prop.setScrollMetric(...); // 移動端優化參數
#else prop.setScrollMetric(...); // 桌面端參數
#endif
結語:滾動交互的未來
隨著Qt6對QScroller的持續優化,已經未來產品更趨向于實際流暢感的需求,作為開發使用人員,這幾個方面的優化值得我們關注使用:
- 多指操作與3D滾動集成
- 基于AI的動態參數調優
- 與Vulkan渲染引擎的深度結合