qt浏览文件支持惯性
#include <QApplication>
#include <QListWidget>
#include <QScroller>
#include <QScrollerProperties>int main(int argc, char *argv[]) {QApplication app(argc, argv);// 创建列表控件并添加示例项QListWidget listWidget;for (int i = 0; i < 100; ++i) {listWidget.addItem(QString("文件 %1").arg(i));}listWidget.resize(400, 300);listWidget.show();// 启用惯性滚动:触摸手势// QScroller::grabGesture(listWidget.viewport(), QScroller::TouchGesture);// 或者启用鼠标左键拖动惯性QScroller::grabGesture(listWidget.viewport(), QScroller::LeftMouseButtonGesture);// 获取Scroller对象并设置参数QScroller *scroller = QScroller::scroller(listWidget.viewport());QScrollerProperties properties = scroller->scrollerProperties();// 调整减速度因子(0~1,值越小惯性越长)properties.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.05);// 设置最小拖动触发距离(防止误触)properties.setScrollMetric(QScrollerProperties::DragStartDistance, 0.001);scroller->setScrollerProperties(properties);return app.exec();
}
示例二:
// inertialscroller.h
#ifndef INERTIALSCROLLER_H
#define INERTIALSCROLLER_H#include <QAbstractItemView>
#include <QEvent>
#include <QMouseEvent>
#include <QObject>
#include <QPointF>
#include <QScrollBar>
#include <QTableView>
#include <QTime>
#include <QTimer>
#include <QWheelEvent>/*** @brief 惯性滚动管理器类** 为QTableView或其他QAbstractItemView子类提供惯性滚动功能。* 追踪鼠标拖拽事件并在释放后应用速度衰减算法模拟惯性滚动效果。*/
class InertialScroller : public QObject
{Q_OBJECTpublic:/*** @brief 构造函数* @param view 需要添加惯性滚动功能的视图* @param parent 父对象*/explicit InertialScroller(QAbstractItemView *view, QObject *parent = nullptr);/*** @brief 析构函数*/~InertialScroller();/*** @brief 设置衰减系数,控制惯性滚动的衰减速度* @param factor 衰减系数(0.0-1.0),越小衰减越快*/void setDecelerationFactor(qreal factor);/*** @brief 设置惯性滚动的最大初始速度* @param speed 最大速度(像素/秒)*/void setMaxSpeed(qreal speed);/*** @brief 设置停止惯性滚动的最小速度阈值* @param threshold 速度阈值(像素/秒)*/void setStopThreshold(qreal threshold);/*** @brief 设置滚轮惯性滚动的强度系数* @param factor 强度系数,数值越大惯性效果越强*/void setWheelSpeedFactor(qreal factor);/*** @brief 启用或禁用滚轮惯性滚动* @param enable 是否启用*/void setWheelInertiaEnabled(bool enable);protected:/*** @brief 事件过滤器* 拦截视图的鼠标事件处理惯性滚动*/bool eventFilter(QObject *watched, QEvent *event) override;private:QAbstractItemView *m_view; // 关联的视图QTimer m_timer; // 惯性滚动计时器QTime m_lastTime; // 上次事件时间QPointF m_lastPos; // 上次鼠标位置QPointF m_velocity; // 当前速度(x和y方向)bool m_isPressed = false; // 鼠标是否按下bool m_isScrolling = false; // 是否正在滚动qreal m_deceleration = 0.95; // 衰减系数(0.0-1.0)qreal m_maxSpeed = 2000.0; // 最大速度(像素/秒)qreal m_stopThreshold = 10.0; // 停止阈值(像素/秒)QVector<QPointF> m_positions; // 最近的鼠标位置记录QVector<qint64> m_timestamps; // 最近的鼠标位置时间戳qreal m_wheelSpeedFactor = 15.0; // 滚轮速度系数bool m_wheelInertiaEnabled = true; // 是否启用滚轮惯性private slots:/*** @brief 执行惯性滚动步骤*/void scrollStep();/*** @brief 开始惯性滚动*/void startScrolling(const QPointF &velocity);/*** @brief 停止惯性滚动*/void stopScrolling();
};#endif // INERTIALSCROLLER_H
// inertialscroller.cpp
#include <QDateTime>
#include <QDebug>
#include <QtMath>#include "inertialscroller.h"InertialScroller::InertialScroller(QAbstractItemView *view, QObject *parent) : QObject(parent), m_view(view)
{// 安装事件过滤器m_view->viewport()->installEventFilter(this);// 初始化计时器m_timer.setInterval(16); // 约60FPSconnect(&m_timer, &QTimer::timeout, this, &InertialScroller::scrollStep);// 初始化历史记录容器m_positions.reserve(10);m_timestamps.reserve(10);
}InertialScroller::~InertialScroller()
{if(m_view && m_view->viewport()){m_view->viewport()->removeEventFilter(this);}m_timer.stop();
}void InertialScroller::setDecelerationFactor(qreal factor)
{// 确保值在有效范围内m_deceleration = qBound(0.1, factor, 0.99);
}void InertialScroller::setMaxSpeed(qreal speed)
{m_maxSpeed = qMax(1.0, speed);
}void InertialScroller::setStopThreshold(qreal threshold)
{m_stopThreshold = qMax(1.0, threshold);
}void InertialScroller::setWheelSpeedFactor(qreal factor)
{m_wheelSpeedFactor = qMax(1.0, factor);
}void InertialScroller::setWheelInertiaEnabled(bool enable)
{m_wheelInertiaEnabled = enable;
}bool InertialScroller::eventFilter(QObject *watched, QEvent *event)
{if(watched != m_view->viewport())return false;switch(event->type()){case QEvent::MouseButtonPress: {QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);if(mouseEvent->button() == Qt::LeftButton){stopScrolling();m_isPressed = true;m_lastPos = mouseEvent->pos();m_lastTime.start();// 清空历史记录m_positions.clear();m_timestamps.clear();// 记录初始位置和时间m_positions.append(mouseEvent->pos());m_timestamps.append(QDateTime::currentMSecsSinceEpoch());}break;}case QEvent::MouseMove: {if(m_isPressed){QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);QPointF currentPos = mouseEvent->pos();// 计算移动距离QPointF delta = m_lastPos - currentPos;// 添加到历史记录m_positions.append(currentPos);m_timestamps.append(QDateTime::currentMSecsSinceEpoch());// 仅保留最近的5个记录点while(m_positions.size() > 5){m_positions.removeFirst();m_timestamps.removeFirst();}// 滚动视图QScrollBar *vScrollBar = m_view->verticalScrollBar();QScrollBar *hScrollBar = m_view->horizontalScrollBar();if(vScrollBar && vScrollBar->isVisible()){vScrollBar->setValue(vScrollBar->value() + delta.y());}if(hScrollBar && hScrollBar->isVisible()){hScrollBar->setValue(hScrollBar->value() + delta.x());}m_lastPos = currentPos;}break;}case QEvent::MouseButtonRelease: {if(m_isPressed){QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);if(mouseEvent->button() == Qt::LeftButton){m_isPressed = false;// 如果有足够的历史数据来计算速度if(m_positions.size() >= 2){// 使用最后几个点的平均速度QPointF totalVelocity(0, 0);int count = 0;for(int i = 1; i < m_positions.size(); ++i){qint64 timeDelta = m_timestamps[i] - m_timestamps[i - 1];if(timeDelta > 0){QPointF posDelta = m_positions[i - 1] - m_positions[i];// 速度 = 距离/时间,单位为像素/秒QPointF velocity = posDelta * (1000.0 / timeDelta);totalVelocity += velocity;count++;}}if(count > 0){QPointF avgVelocity = totalVelocity / count;// 限制最大速度qreal speedX = qBound(-m_maxSpeed, avgVelocity.x(), m_maxSpeed);qreal speedY = qBound(-m_maxSpeed, avgVelocity.y(), m_maxSpeed);// 如果速度足够大,启动惯性滚动QPointF finalVelocity(speedX, speedY);qreal speed = qSqrt(speedX * speedX + speedY * speedY);if(speed > m_stopThreshold){startScrolling(finalVelocity);}}}}}break;}case QEvent::Wheel: {// 如果滚轮惯性被禁用,不拦截事件if(!m_wheelInertiaEnabled){stopScrolling();return false;}// 处理鼠标滚轮事件产生惯性滚动QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);// 停止当前的惯性滚动stopScrolling();// 计算滚轮滚动的速度和方向QPoint pixelDelta = wheelEvent->pixelDelta();QPoint angleDelta = wheelEvent->angleDelta();// 优先使用像素增量,如果没有则使用角度增量QPointF scrollAmount;if(!pixelDelta.isNull()){scrollAmount = QPointF(pixelDelta);} else if(!angleDelta.isNull()){// 标准鼠标滚轮:角度转为像素(大约8度为一个标准滚动单位)scrollAmount = QPointF(angleDelta) / 8.0;}// 应用滚动方向(正数向下/右,负数向上/左)scrollAmount = -scrollAmount;// 缩放滚动量来创建合适的惯性效果QPointF velocity = scrollAmount * m_wheelSpeedFactor;// 如果速度足够大,开始惯性滚动qreal speed = qSqrt(velocity.x() * velocity.x() + velocity.y() * velocity.y());if(speed > m_stopThreshold){startScrolling(velocity);// 先手动执行一次滚动,让响应更快QScrollBar *vScrollBar = m_view->verticalScrollBar();QScrollBar *hScrollBar = m_view->horizontalScrollBar();if(vScrollBar && vScrollBar->isVisible() && !angleDelta.isNull()){vScrollBar->setValue(vScrollBar->value() + angleDelta.y() / 120 * vScrollBar->singleStep());}if(hScrollBar && hScrollBar->isVisible() && !angleDelta.isNull()){hScrollBar->setValue(hScrollBar->value() + angleDelta.x() / 120 * hScrollBar->singleStep());}return true; // 拦截滚轮事件,自己处理}break;}default:break;}// 继续传递事件,不拦截return false;
}void InertialScroller::scrollStep()
{if(!m_isScrolling || m_isPressed)return;// 减速m_velocity *= m_deceleration;// 计算滚动距离qreal dx = m_velocity.x() * (m_timer.interval() / 1000.0);qreal dy = m_velocity.y() * (m_timer.interval() / 1000.0);// 应用滚动QScrollBar *vScrollBar = m_view->verticalScrollBar();QScrollBar *hScrollBar = m_view->horizontalScrollBar();if(vScrollBar && vScrollBar->isVisible()){vScrollBar->setValue(vScrollBar->value() + qRound(dy));}if(hScrollBar && hScrollBar->isVisible()){hScrollBar->setValue(hScrollBar->value() + qRound(dx));}// 如果速度足够小,停止滚动qreal speed = qSqrt(m_velocity.x() * m_velocity.x() + m_velocity.y() * m_velocity.y());if(speed < m_stopThreshold){stopScrolling();}
}void InertialScroller::startScrolling(const QPointF &velocity)
{if(m_isScrolling)return;m_velocity = velocity;m_isScrolling = true;m_timer.start();
}void InertialScroller::stopScrolling()
{if(!m_isScrolling)return;m_timer.stop();m_isScrolling = false;m_velocity = QPointF(0, 0);
}