QT January 14, 2020

【QT】QListWidget、QScrollArea等添加触摸滚动和惯性滚动

Words count 8k Reading time 7 mins. Read count 0

前言

平时使用QListWidget/QScrollArea等内容视图可以滚动的控件时,一般是通过它们的滚动条来实现滚动的。在智慧课堂项目中,实施的学校使用的是一体机–window系统可触摸的大屏。当然也可以通过滚动条来拖动,但这对于习惯于手机触摸交互的用户来说,操作起来是很不友好的。因此,需要让这些控件支持触摸滚动。主要的实现方式有两种:

  1. 通过自定义控件,子类化这些滚动控件,重新实现它们的mousePressEvent、mouseMoveEvent和mouseReleaseEvent等事件处理函数
  2. 通过使用QScroller

步骤

下边以QListWidget为例,展示如何实现QListWidget的触摸滚动:

方式一:

  1. 创建一个MyListWidget,基类为QListWidget
  2. 添加mousePressEvent、mouseMoveEvent和mouseReleaseEvent三个函数的声明
  3. 通过动态改变horizontalScrollBar()->setValue、verticalScrollBar()->setValue实现横向和垂直的滚动

方式二:

  1. 调用setVerticalScrollMode、setHorizontalScrollMode,将滚动模式设置为ScrollPerPixel
  2. 调用QScroller的静态函数grabGesture

具体代码

方式一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 头文件
class MyListWidget : public QListWidget
{
Q_OBJECT

public:
explicit MyListWidget(QWidget *parent);
~MyListWidget();

protected:
void mousePressEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *ev) Q_DECL_OVERRIDE;

private:
bool m_pressed;
QPoint m_lastPoint;
};

// cpp文件
MyListWidget::MyListWidget(QWidget *parent)
: QListWidget(parent)
, m_pressed(false)
{
setAttribute(Qt::WA_TranslucentBackground);
}

MyListWidget::~MyListWidget()
{
//QScroller::ungrabGesture(this);
}

void MyListWidget::mousePressEvent(QMouseEvent *ev)
{
m_pressed = true;
m_lastPoint = ev->pos();
QListWidget::mousePressEvent(ev);
}

void MyListWidget::mouseMoveEvent(QMouseEvent *ev)
{
if (m_pressed)
{
int horiDelta = ev->pos().x() - m_lastPoint.x();
int vertDelta = ev->pos().y() - m_lastPoint.y();
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - horiDelta);
verticalScrollBar()->setValue(verticalScrollBar()->value() - vertDelta);

m_lastPoint = ev->pos();
}
QListWidget::mouseMoveEvent(ev);
}

void MyListWidget::mouseReleaseEvent(QMouseEvent *ev)
{
m_pressed = false;
QListWidget::mouseReleaseEvent(ev);
}

方式二:

1
2
3
// 没看错,就是两句代码的事情
ui.m_testListWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel);
QScroller::grabGesture(ui.m_testListWidget, QScroller::LeftMouseButtonGesture);

其他补充

  1. 推荐使用方式二,因为方式二除了能实现触摸滚动外,QScroller本身还做了滚动优化,支持惯性滚动:普通滚动(即实现方式一),当手指从触摸屏上移开,滚动立即停止;使用惯性滚动,当手指从触摸屏上移开,内容会保持一段时间的滚动效果,继续滚动的速度和持续的时间和滚动手势的强烈程度成正比
  2. QScroller手势识别器支持的不同手势类型,例如QScroller::TouchGesture、QScroller::LeftMouseButtonGesture等,由于一体机的触摸实现,本质是将触摸事件转换成了触摸事件,因此我上边的例子使用的是QScroller::LeftMouseButtonGesture,假如是移动设备,可以使用QScroller::TouchGesture
  3. 使用QScroller,默认是使用了OvershootAlwaysOn,即滚动视图有个过冲回弹的效果,可以通过修改QScroller的属性禁止这个过冲效果
    1
    2
    3
    4
    5
    6
    QScroller::grabGesture(ui.m_testListWidget, QScroller::LeftMouseButtonGesture);
    QScrollerProperties propertiesOne = QScroller::scroller(ui.m_testListWidget)->scrollerProperties();
    QVariant overshootPolicyOne = QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff);
    propertiesOne.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, overshootPolicyOne);
    propertiesOne.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, overshootPolicyOne);
    QScroller::scroller(ui.m_testListWidget)->setScrollerProperties(propertiesOne);
0%