20.3 降低闪烁

闪烁问题是所有GUI程序员心中永远的痛.通常所有的应用程序都需要想办法来降低可能的闪烁,下面我们就这方面的话题来给您一些有益的提示.

在Windows平台上,如果你的窗口正在使用wxFULL_REPAINT_ON_RESIZE类型,尽量去掉它.这将导致窗口系统只重绘那些由于改变大小或者别的操作而被"破坏"的那部分而不是整个窗口,这将降低擦除和重画操作的范围.否则,即使窗口只是改变一点点的大小,整个窗口都将被重画,从而导致屏幕闪烁.不过如果你的程序中,主窗口显式的内容是和窗口的大小有关的,这种情况下,这种方法毫无用处,因为整个窗口必须被重新绘制.

有时候你可以设置wxCLIP_CHILDREN类型,这将阻止一个窗口发生改变的时候同时刷新它的子窗口.不过这个类型在别的平台上没有影响.

当你在滚动窗口上绘制的时候,你可以采取很多步骤来提供重绘的效率以减小闪烁.首先,你需要优化你获得绘制数据的方式:你只需要搜集那些可以在当前的可视区域内显示的数据(参考wxWindow::GetViewStart函数和wxWindow:: GetClientSize函数手册),而且在你的重画函数内,你也可以只重画那些处于需要更新区域内的图形(参考wxWindow:: GetUpdateRegion).在设计数据结构的时候,你应该考虑怎样设计才能使得你可以快速访问到当前视图对应的数据,比如说,如果你的每个数据项都有相同的宽度的时候,你可以使用链表或者数据来存放这些数据,这比你挨个搜索所有的数据要快速的多.如果计算当前位置是一个很耗时的操作,你可以考虑对最近访问的页面的起始数据位置进行缓存.然后你可以直接通过缓存快速的进行上一页下一页这样的起始数据定位动作.你也可以为每块数据增加一个用来记录其高度的字段,以便不必每次都需要计算这个数据段的高度.

当你需要实现可以滚动的图片时,你可以使用wxWindow::ScrollWindow函数,来对窗口进行物理滚动,这将使得你只需要更新剩下的很小的区域,这将大大降低闪烁.(wxScrolledWindow类已经为你实现了这种机制,GetUpdateRegion函数将反应你需要更新的这个很小的区域.)

正如我们在第5章,"绘画和打印"中介绍的那样,你还可以定义你自己的背景擦除事件处理函数,并将其留空,以禁止控件自己清除自己的背景.然后你可以直接在旧的图片上更新整个图片(包括整个背景所在的范围),以便降低由于在绘图前擦除背景导致的屏幕闪烁.使用wxWindow:: SetBackgroundStyle函数将背景类型设置为wxBG_STYLE_CUSTOM将阻止控件自作主张的更新背景.第5章还介绍了 wxBufferedDC和wxBufferedPaintDC,你可以结合前面提到的这些技术一起使用来降低闪烁.

另外一种情况是由于对一个窗口进行多次连续的更新导致窗口闪烁.wxWidgets提供了wxWindow::Freeze函数和 wxWindow::Thaw函数,这两个函数可以控制窗口在被更新的时候是否立即显示在屏幕上.比如说,在你需要往一个文本框中增加很多行文本的时候, 或者往一个列表框中增加很多个子项的时候,你可以使用这两个函数.当Thaw函数被调用的时候,窗口才会进行彻底刷新.在Windows系统和Mac OSX系统上,所有的wxWindow类都支持Freeze和Thaw函数,而在GTK+平台上,wxTextCtrl也支持这两个函数.你也可以在自己的控件中实现这两个函数来避免过度的更新用户界面(第12章介绍的wxThumbnailCtrl例子实现了这两个函数,你可以参考 examples/chap12/thumbnail目录中的代码).