Qt Snippet: Use QScroller with QScrollArea

In case you are developing a Qt app for mobile device you'll have the "problem" to manage widgets that need to be scrolled for show data inside. In Desktop system this work is done by using scrollbars or the wheel mouse event but in mobile system you can not use same ways. Instead the touch event is the only way the user have to scroll your widget.

The common way in this case is to use a mechanism called kinetic scrolling. Qt already provide a class for easily add this functionality called QScroller. However it require to know some small tips for use in a correct way. In case you want to "apply" kinetic scrolling to a QScrollArea widget (but the same is true for other list widgets like QListWidget, QTableWidget and QTreeWidget) the common way showed into examples is the following:

QScroller::grabGesture(pMyContainerArea, QScroller::LeftMouseButtonGesture);

However, testing the result, you'll note the scroller doesn't work as expected since it scroll the entire widget and not the content inside. This because the QScrollArea widget manage the content to scroll using an internal viewport. This mean if you want to obtain a "correct" scrolling result you have to change the code as follow:

QScroller::grabGesture(pMyContainerArea->viewport(), QScroller::LeftMouseButtonGesture);

As you can see in this case the kinetic scrolling is applied to the content inside the main widget (the viewport area). Now the scrolling work as expected however there is an additional strange behaviour. By default the QScroller object move the scrolling area "over" the natural limits. Once started the scrolling phase by touch over and move you'll note you can move the content outside the main widget area and, once released by untouch, the content will come back with an "elastic effect" that look a bit strange. For avoid such behaviour you can simply set the correct scroller property bu using the following code:

QVariant OvershootPolicy = QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff);
QScrollerProperties ScrollerProperties = QScroller::scroller(pMyContainerArea->viewport())->scrollerProperties();
ScrollerProperties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, OvershootPolicy);
ScrollerProperties.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, OvershootPolicy);

Now the kinetic scrolling will work as expected.


Oh man! You saved my whole day. Thank you so much.

December 4, 2016 at 6:39 AM comment-delete

Post a Comment