如何避免 Swing 结合 repaint() 请求?
How to avoid Swing combining repaint() requests?
Swing 通常会合并多个 repaint() 请求,以便仅驱动 paintComponent() 方法一次,从而提高性能。通常,这就是您想要的。
然而,在我的应用程序中,这是不可取的。我的组件是一个充满文本的大屏幕(就像一个文本编辑器)。每个字符都包含 foreground/background 颜色和样式(如粗体)等属性。重绘整个屏幕(repaint(() 默认执行)是一项昂贵的操作,并且在仅更新屏幕上的几个不同字符时会引入不必要的延迟。
因此,我多次调用 repaint(x, y, width, height) ,但仅针对那些实际发生变化的区域(通常为 50 个字符或更少,可能会在两到三个时间内散布在屏幕上独立的连续区域)。
问题是,通常我会在屏幕的顶部和底部更改字符。我为每个受影响的区域调用重绘,但 Swing 通过计算要重绘的区域的并集将我的多个重绘 (...) 请求合并为一个重绘 (...) 请求(这是默认行为)。这会导致对 paintComponent() 的一次调用,但会产生一个包含所有要更新区域的大剪裁矩形(由 getClipBounds() 返回)。因此,无论如何(基于 ClipBounds),我最终都会重新绘制大面积的屏幕,这会适得其反,也是我希望避免的,因为实际上只有一小部分屏幕被修改,因此需要重绘。
问题:有什么方法可以减轻这种行为,使我的 paintComponent() 方法只重绘那些已修改的区域,并避免对未修改的区域进行不必要的重绘?
其他详细信息,按要求提供:
class 派生自 JComponent。基本上,它只是一个 "empty" window,我使用 Swing 的 API 在其上绘制文本字符。 window 的最大大小约为每行 83 行 x 320 列(这是在 Apple Cinema 显示器上),因此对于 8x16 字体,即 2560x1328。
基本上,该应用程序是一个文本编辑器(想想:vi)。显示屏的底行是状态行。在最坏的情况下,我可能会将光标移动到 window 顶部右侧的一个位置。这将导致更新 window 底部的状态行以反映新的光标位置(行、列)。因此,我们在 window 的顶部更改了一些位置,在 window 的底部更改了一些位置。我会发出 2 个 repaint(...) 请求,一个用于 window 的每个修改区域。但是,repaint() 将合并这两个请求,并调用 paintComponent() 一次,边界矩形(定义要重绘的区域)是两个区域的 union实际上更新了。生成的矩形将非常大,从 window 的顶部延伸到底部。因此,在 paintComponent() 中,我最终重新绘制了大部分屏幕,即使其中绝大部分没有被修改。这就是我要避免的。
Is there any way to mitigate this behavior, so that my paintComponent() method repaints only those areas that have been modified,
您可以尝试使用 JComponent
class 的 paintImmediately(...)
方法。
这将导致立即完成多个较小的绘制请求,而没有 RepaintManager
的好处。只有您可以决定多个较小的请求是否比单个较大的绘制请求执行得更好。
Swing 通常会合并多个 repaint() 请求,以便仅驱动 paintComponent() 方法一次,从而提高性能。通常,这就是您想要的。
然而,在我的应用程序中,这是不可取的。我的组件是一个充满文本的大屏幕(就像一个文本编辑器)。每个字符都包含 foreground/background 颜色和样式(如粗体)等属性。重绘整个屏幕(repaint(() 默认执行)是一项昂贵的操作,并且在仅更新屏幕上的几个不同字符时会引入不必要的延迟。
因此,我多次调用 repaint(x, y, width, height) ,但仅针对那些实际发生变化的区域(通常为 50 个字符或更少,可能会在两到三个时间内散布在屏幕上独立的连续区域)。
问题是,通常我会在屏幕的顶部和底部更改字符。我为每个受影响的区域调用重绘,但 Swing 通过计算要重绘的区域的并集将我的多个重绘 (...) 请求合并为一个重绘 (...) 请求(这是默认行为)。这会导致对 paintComponent() 的一次调用,但会产生一个包含所有要更新区域的大剪裁矩形(由 getClipBounds() 返回)。因此,无论如何(基于 ClipBounds),我最终都会重新绘制大面积的屏幕,这会适得其反,也是我希望避免的,因为实际上只有一小部分屏幕被修改,因此需要重绘。
问题:有什么方法可以减轻这种行为,使我的 paintComponent() 方法只重绘那些已修改的区域,并避免对未修改的区域进行不必要的重绘?
其他详细信息,按要求提供:
class 派生自 JComponent。基本上,它只是一个 "empty" window,我使用 Swing 的 API 在其上绘制文本字符。 window 的最大大小约为每行 83 行 x 320 列(这是在 Apple Cinema 显示器上),因此对于 8x16 字体,即 2560x1328。
基本上,该应用程序是一个文本编辑器(想想:vi)。显示屏的底行是状态行。在最坏的情况下,我可能会将光标移动到 window 顶部右侧的一个位置。这将导致更新 window 底部的状态行以反映新的光标位置(行、列)。因此,我们在 window 的顶部更改了一些位置,在 window 的底部更改了一些位置。我会发出 2 个 repaint(...) 请求,一个用于 window 的每个修改区域。但是,repaint() 将合并这两个请求,并调用 paintComponent() 一次,边界矩形(定义要重绘的区域)是两个区域的 union实际上更新了。生成的矩形将非常大,从 window 的顶部延伸到底部。因此,在 paintComponent() 中,我最终重新绘制了大部分屏幕,即使其中绝大部分没有被修改。这就是我要避免的。
Is there any way to mitigate this behavior, so that my paintComponent() method repaints only those areas that have been modified,
您可以尝试使用 JComponent
class 的 paintImmediately(...)
方法。
这将导致立即完成多个较小的绘制请求,而没有 RepaintManager
的好处。只有您可以决定多个较小的请求是否比单个较大的绘制请求执行得更好。