前言
平时使用QListWidget/QScrollArea等内容视图可以滚动的控件时,一般是通过它们的滚动条来实现滚动的。在智慧课堂项目中,实施的学校使用的是一体机–window系统可触摸的大屏。当然也可以通过滚动条来拖动,但这对于习惯于手机触摸交互的用户来说,操作起来是很不友好的。因此,需要让这些控件支持触摸滚动。主要的实现方式有两种:
- 通过自定义控件,子类化这些滚动控件,重新实现它们的mousePressEvent、mouseMoveEvent和mouseReleaseEvent等事件处理函数
- 通过使用QScroller
步骤
下边以QListWidget为例,展示如何实现QListWidget的触摸滚动:
方式一:
- 创建一个MyListWidget,基类为QListWidget
- 添加mousePressEvent、mouseMoveEvent和mouseReleaseEvent三个函数的声明
- 通过动态改变horizontalScrollBar()->setValue、verticalScrollBar()->setValue实现横向和垂直的滚动
方式二:
- 调用setVerticalScrollMode、setHorizontalScrollMode,将滚动模式设置为ScrollPerPixel
- 调用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);
其他补充
- 推荐使用方式二,因为方式二除了能实现触摸滚动外,QScroller本身还做了滚动优化,支持惯性滚动:普通滚动(即实现方式一),当手指从触摸屏上移开,滚动立即停止;使用惯性滚动,当手指从触摸屏上移开,内容会保持一段时间的滚动效果,继续滚动的速度和持续的时间和滚动手势的强烈程度成正比
- QScroller手势识别器支持的不同手势类型,例如QScroller::TouchGesture、QScroller::LeftMouseButtonGesture等,由于一体机的触摸实现,本质是将触摸事件转换成了触摸事件,因此我上边的例子使用的是QScroller::LeftMouseButtonGesture,假如是移动设备,可以使用QScroller::TouchGesture
- 使用QScroller,默认是使用了OvershootAlwaysOn,即滚动视图有个过冲回弹的效果,可以通过修改QScroller的属性禁止这个过冲效果
1
2
3
4
5
6QScroller::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);