Reputation: 395
This is a fragment of an application with several tabs hung off a JTabbedPanel:-
I have generated the image with code in the standard following manner:-
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(device.getVisualisation(), 0, 0, null);
}
So every time something happens to the application gui, the paintComponent method is called and the image displays. That's normal.
This paint event also happens if the tab focus changes, or the mouse passes over the tab as you'd expect. The problem is this takes several seconds as generating the graphic takes a lot of time. This delay is unavoidable and I accept it. You also get many paint events as the gui system does what it has to. Normally this would be okay, but with the processing delay, the gui stops /flashes /updates several times over the period of say 10 seconds.
I thought that I could deal with this by only manually calling repaint() from a "REFRESH" JButton somewhere on the gui. But I can't turn off the automatic repainting if you tough the tabs. How do I only paint a component by pressing a button, and not automatically?
Upvotes: 0
Views: 92
Reputation: 285403
I would do things a bit differently by first figuring out what is the expensive process here. It's not painting but rather the calculation and creation of the special image that is displayed by the painting. So with this in mind, rather than turning off painting, which would involve only kludges, instead store the image drawn an image field, and carefully control when it is recreated via the expensive device.getVisualisation()
method.
If this method is truly long-running, then it don't call it within paintComponent, a method which never should hold cpu-intense or time crunching code, and in fact, the method should be called off of the Swing event thread, and instead within a background thread. Then when the background thread is done processing, update that same BufferedImage and call repaint()
, and display the new image.
For example:
private Image image = null; // holds our image
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (image != null) {
// this will hardly take any time at all to run
g2d.drawImage(image, 0, 0, this);
}
}
public void myDrawImage() {
// create a SwingWorker for background threading work
new SwingWorker<Image, Void>() {
@Override
protected Image doInBackground() throws Exception {
// run this long-running code within this background thread
return device.getVisualisation();
};
@Override
protected void done() {
try {
// when the thread is done, get the new image,
// put it into our image field, and repaint the component
image = get();
repaint();
} catch (InterruptedException | ExecutionException e) {
// TODO handle any exceptions that occur with drawing
}
};
}.execute();
}
Now the image drawn will only change when and if your program specifically calls the myDrawImage()
method, so that now the calling of the long-running code is under your total control.
Upvotes: 2