前段时间做过一个界面刷新的优化,遇到的坑比较多,在这里做一点点总结吧。
优化的方案是滚动滚动条的时候用截屏的方式代替界面全部刷新,优化完成后,界面在滚动时效率能提升大概一倍,背景介绍完毕。
用到最主要的是QT的截屏功能
window原生api会提供截屏滚动的功能。可以用这个ScrollWindowEx这个api。它会根据相应的参数在屏幕中进行滚动相应的区域。是不是很容易?但是结果却是不理想,因为用的是Qt,控件重写过PaintEvent的方法。调用api实时能看到效果,但是触发一次PaintEvent之后,界面又恢复原样了。看了Qt源码里后发现Qt内部画的时候是相当于画在后台一个对象里,然后在刷新的时候调用bitblt统一画,调用window的api并不会影响到后面的那个对象,后台刷新的代码在:qwindowsbackingstore.cpp中的flush方法(Qt5.1.1是这个名,但在Qt5.5.1里叫flushDP)。
这个或许应该是Qt的特点,把操作系统隔离开,有好的地方,在这里又觉得不好。
Qt里截屏的方式有以下两个,功能如下:
1:QScreen的grabWindow:不会触发paintEvent消息
2:QWidget的grab:会触发paintEvent消息,相当于是调用paintEvent在一张图上画。
其它还可以调用QPixmap的两个静态方法(grabWidget跟grabWindow),但看源代码发现不推荐用这两个,应该用上面那两个替代,实现的效果应该是一样的。
基于业务的需要,很明显得采用1,实现的代码也很简单
QScreen *pScreen = QGuiApplication::primaryScreen();
pImage = pScreen->grabWindow(pWidget->winId());
如果需要实现滚动的话,需要调用QPixmap的scroll的方法就可以实现了。然后在PaintEvent里先把截出来的图画上去,然后再画剩下的部分就可以了。
现在理解的Qwidget的刷新机制是这样的。刷新界面一般都会调用widget的update方法。这个方法在文档里有这么一句
calling update() several times normally results in just one paintEvent() call。
跟了一下这里的源码,也发现,多次调用的活,它会在里面记录一个dirtyRegion,多次调用的话,会把区域合并在一块,在触发paint消息里统一处理。
画的时候,会根据dirtyRegion的区域只处理那部分。如果你贴图贴了整个窗口的大小,后面重绘了部分区域,很可能会画重,导致显示重叠,这点注意。
由于业务上比较复杂,截屏的区域需要去算,但原理上应该是上面提到的那样,本人思路有限,如果有更好的实现方法,也可以一块交流,在此表示感谢。
http://blog.csdn.net/hpjx1987/article/details/50634194