SWT Canvas绘制多矩形触发Graphic is disposed错误排查求助
Let's break down why you're hitting this error and fix it step by step:
Root Causes of the Error
Looking at your error stack trace and code, there are three critical issues causing the Graphic is disposed exception:
You're manually disposing the framework-managed GC
In everypaintControlmethod, you calle.gc.dispose(). TheGCpassed viaPaintEventis created and owned by the SWT framework—you never need to dispose it yourself. When you do, the framework tries to use this already-released GC later, triggering the disposed error.You're adding duplicate PaintListeners and disposing the Canvas
Your timer task adds a newPaintListenertocanvas2every second, and inside each listener you callcanvas2.dispose(). Oncecanvas2is disposed, any subsequent paint events (triggered by the framework or your code) will try to access a destroyed component, leading to errors.Inefficient and error-prone drawing logic
Adding new listeners for each rectangle is not how SWT drawing works. You should maintain a collection of drawing data, then render all elements in a single listener when a redraw is triggered.
Fixed Code Implementation
Here's how to adjust your code to resolve these issues:
First, add an inner class to store rectangle data (position, size, color) and a list to track all rectangles you want to draw:
public class MainWindow implements Runnable { // Inner class to hold rectangle drawing details private static class RectangleInfo { int x; int y; int width; int height; Color color; public RectangleInfo(int x, int y, int width, int height, Color color) { this.x = x; this.y = y; this.width = width; this.height = height; this.color = color; } } private List<RectangleInfo> rectangles = new ArrayList<>(); // ... rest of your class variables
Next, update your canvas2 PaintListener to render all rectangles from the list (only add this listener once, not in the timer):
canvas2.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { // Render every rectangle in our list for (RectangleInfo rect : rectangles) { e.gc.setBackground(rect.color); e.gc.fillRectangle(rect.x, rect.y, rect.width, rect.height); // Fill the rectangle e.gc.setForeground(rect.color); e.gc.drawRectangle(rect.x, rect.y, rect.width, rect.height); // Draw border (optional) } // DO NOT call e.gc.dispose() here! } });
Then, rewrite your timer runnable to add rectangle data to the list and trigger a redraw, instead of adding new listeners:
final Runnable run = new Runnable() { private int counter = 0; @Override public void run() { // Move the image lblNewLabel.setLocation(lblNewLabel.getLocation().x + 1, lblNewLabel.getLocation().y); // Update composite_1 size composite_1.setSize(composite_1.getSize().x + 1, 282); composite_1.setBackground(green); int random = (int) (Math.random() * 10); Color rectColor = red; // Default color switch (random) { case 0: rectColor = red; break; case 1: rectColor = gray; break; case 2: rectColor = darkBlue; break; default: // Skip drawing for other random values display.timerExec(1000, this); return; } // Add new rectangle data to our list rectangles.add(new RectangleInfo(composite_1.getSize().x, 0, 5, 15, rectColor)); // Trigger canvas2 to redraw with the new rectangle canvas2.redraw(); System.out.print(random); display.timerExec(1000, this); } };
Finally, remove the unnecessary redrawCanvas method (it disposes a GC incorrectly) and all instances of e.gc.dispose() and canvas2.dispose() from your code.
Key SWT Drawing Best Practices
- Never dispose the GC from PaintEvent: SWT manages this resource—let it handle cleanup.
- Use a single PaintListener: Maintain drawing data in a collection, then render all elements in one place.
- Trigger redraws with
redraw(): Instead of adding new listeners, update your data and callredraw()to tell SWT to repaint the component. - Avoid disposing components while they're in use: Only dispose a component when you're completely done with it (e.g., when the shell closes).
内容的提问来源于stack exchange,提问作者Duseop

