Swing normally combines multiple repaint() requests, in order to drive the paintComponent() method only once and thus enhance performance. Normally, this is what you want.
In my application, however, this is undesirable. My component is a large screen full of text (like a text editor). Each character includes attributes like foreground/background color and styles (like BOLD). Repainting the entire screen (which repaint(() does by default) is an expensive operation, and introduces unnecessary latency, when only updating a few disparate characters on-screen.
Therefore, I call repaint(x, y, width, height) multiple times, but only for those areas that have actually changed (typically, 50 characters or less, which may be scattered about the screen in two or three separate, contiguous areas).
The problem is, typically I will have characters change at the top and bottom of the screen. I call repaint for each of the affected areas, but Swing combines my multiple repaint(...) requests into a single repaint(...) request (this is default behavior) by computing the union of the areas to be repainted. This results in a single call to paintComponent() but with a large clipping rectangle (as returned by getClipBounds()) which encompasses all areas to be updated. Thus, I wind up repainting a large area of the screen, anyway (based on the ClipBounds), which is counter-productive and what I wish to avoid, since only a small portion of the screen has actually been modified and therefore needs to be repainted.
Question: Is there any way to mitigate this behavior, so that my paintComponent() method repaints only those areas that have been modified, and avoids unnecessary repainting of unmodified areas?
Additional detail, as requested:
The class is derived from JComponent. Basically, it's just an "empty" window on which I draw text characters using Swing's API. The window's maximum size is about 83 rows by 320 columns per row (this is on an Apple Cinema display), so for an 8x16 font, that's 2560x1328.
Basically, the application is a text editor (think: vi). The bottom line of the display is a Status Line. In a worst-case scenario, i might move the cursor one position to the right at the top of the window. This would cause the Status Line at the bottom of the window to be updated to reflect the new cursor position (row, column). So, we have a few locations changing at the top of the window, and a few locations changing at the bottom of the window. I would issue 2 repaint(...) requests, one for each modified area of the window. However, repaint() will combine those two requests, and call paintComponent() once with a bounding rectangle (which defines the area to be repainted) that is the union of the two areas that were actually updated. The resultant rectangle will be very large, extending from the top of the window to the bottom. Thus, in paintComponent(), I wind up repainting a large part of the screen, even thought the vast majority of it was not modified. That is what I'm trying to avoid.
Is there any way to mitigate this behavior, so that my paintComponent() method repaints only those areas that have been modified,
You could try using the paintImmediately(...)
method of the JComponent
class.
This will result in multiple smaller paint requests being done right away without the benefit of the RepaintManager
. Only you can decide is multiple smaller requests performs better than a single larger paint request.