Reputation: 1032
I have:
And I wish to, at the press of the JButton bring up a new JFrame displaying the Canvas subclass as it animates.
The problem I face right now is that the new JFrame appears, however it doesn't get a chance to render anything and the JButton on the main frame stays depressed. The logic I figure behind this is that the EDT hasn't finished doing it's jobs such as showing the JButton as released and so does not get a chance to run the animation method and ends up in deadlock.
This logic treated me well in the past as I made this work by creating a new thread, but having learned more about Java, threads and Swing lately I've come to know that all Swing related events must be handled on one thread: the EDT.
This confuses me as to how I got it working before but lead me to believe that using invokeLater would help the problem; as the job of making the JFrame visible and showing animation would be placed at the end of the queue allowing the JButton to unrelease etc. I've had no luck however; have I completely misunderstood something?
Thanks!
(Also please no comments on my use of the Canvas class as opposed to JPanel, I have my reasons).
Sample code:
Test5 (class with main method).
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*
public class Test5 {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Test5().setup();
}
});
}
private void setup() {
JFrame frame = new JFrame("Test");
JButton button = new JButton("Click here");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
newFrame();
}
});
}
});
frame.getContentPane().add(button);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
private void newFrame() {
JFrame newFrame = new JFrame("The new frame");
newFrame.setVisible(true);
newFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
CanvasClass canvas = new CanvasClass();
newFrame.getContentPane().add(canvas);
newFrame.pack();
canvas.runAnimation();
}
}
CanvasClass (Canvas subclass)
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
public class CanvasClass extends Canvas {
int x;
public CanvasClass() {
setSize(new Dimension(550,550));
this.x = (int) (Math.random() * 255);
}
//@Override
public void paint(Graphics g) {
g.setColor(new Color(x, x, x));
g.fillOval(0,0,500,500);
}
void runAnimation() {
while (true) {
randomise();
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void randomise() {
x = (int) (Math.random() * 255);
}
}
Upvotes: 0
Views: 263
Reputation: 57381
You actualy invoke it in EDT but it's blocked in the canvas.runAnimation();
Place the code to be executed in a separate Thread
(where you can call sleep) but call the repaint()
in SwingUtilities.invokeLater()
Or even better to define a javax.swing.Timer
and call the runAnimation()
in the Timer's actionPerformed()
UPDATE:
int delay = 20; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
canvasInstance.randomise();
canvasInstance.repaint();
}
};
new Timer(delay, taskPerformer).start();
to be called instead of the runAnimation()
Upvotes: 3