Reputation: 41
So, for a personal project, I've been trying to create a program in Java that can make a rudimentary animation appear on a Swing application. As far as I know, I've done everything right, and as far as I know it's working, but when I run the application, it does not let me close the application without Task Manager, and when I force quit the app IntelliJ tells me "process finished with exit code 1". It's also not displaying my animation on the screen despite displaying normal Graphics things such as lines.
Here is my JFrame code:
package animtest;
import javax.swing.*;
public class AnimTest extends JFrame {
public void addComponents() {
AnimPanel panel = new AnimPanel();
setContentPane(panel);
}
public AnimTest(String string) {
super(string);
}
public static void main(String[] args) {
AnimTest frame = new AnimTest("Animation Test");
frame.addComponents();
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Here is my JPanel code:
package animtest;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class AnimPanel extends JPanel {
Image[] marsExploding = new Image[3];
public AnimPanel() {
try {
marsExploding[0] = ImageIO.read(new File("res/MarsExploding.png"));
marsExploding[1] = ImageIO.read(new File("res/MarsExploding2.png"));
marsExploding[2] = ImageIO.read(new File("res/MarsExploding3.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void paintComponent(Graphics g2) {
Graphics2D g = (Graphics2D) g2.create();
g.setColor(Color.white);
for (int i = 0; i < marsExploding.length; i++) {
g.drawImage(marsExploding[i], (getWidth() / 2) - 128, (getHeight() / 2) - 128, 256, 256, null);
try {
TimeUnit.SECONDS.sleep(500);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
g.fillRect(0, 0, getWidth(), getHeight());
}
g.dispose();
repaint();
}
}
Any help is greatly appreciated, thank you!
EDIT 1
OK, so this is my new panel code, which should honor Swing's contract and concurrency:
package animtest;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
public class AnimPanel extends JPanel {
Image[] marsExploding = new Image[3];
Graphics2D g;
int currentFrame = 0;
public AnimPanel() {
try {
marsExploding[0] = ImageIO.read(new File("res/MarsExploding.png"));
marsExploding[1] = ImageIO.read(new File("res/MarsExploding2.png"));
marsExploding[2] = ImageIO.read(new File("res/MarsExploding3.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(500, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
displayNewFrame(currentFrame);
if (currentFrame < 2) {
currentFrame++;
} else {
currentFrame = 0;
}
}
});
timer.start();
}
@Override
public void paintComponent(Graphics g2) {
super.paintComponent(g2);
g = (Graphics2D) g2.create();
}
public void displayNewFrame(int frame) {
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(marsExploding[frame], (getWidth() / 2) - 128, (getHeight() / 2) - 128, 256, 256, null);
}
}
However this doesn't actually display anything to the screen.
Upvotes: 0
Views: 355
Reputation: 347314
Swing is a single threaded framework, this means, that any call which is long running or blocking made from within the context of the Event Dispatching Thread will prevent the UI from been updated or allow the user to interact with it, making your UI appear as if it's hung (because it essentially has).
See Concurrency in Swing for more details.
Instead of trying to use TimeUnit.SECONDS.sleep(500);
inside a paint
method, you should have some background thread which ticks at a regular interval and allows you to update the UI accordingly. The problem is, Swing is also not thread safe, meaning that you should never try and create or update the UI from outside the context of the Event Dispatching Thread.
For a basic solution, you can, however, use a Swing Timer
, which will trigger a ActionListener
on a regular bases within the context of the EDT, making it safe to use with the UI.
See How to use Swing Timers for more details.
Painting in Swing is performed by a series of chained method calls, custom painting requires you to insert your code within on of these links, paintComponent
been the most preferred.
However, you are expected to honor the contract of these links by calling the super
paint method you are overriding.
See Painting in AWT and Swing and Performing Custom Painting for more details
Upvotes: 2