I’m trying to make an application that runs an animation. To do this I’ve got a Jframe that contains my subclass of Jpanel in which the animation runs. Here are my two classes:
Firstly, here’s my driver class:
import javax.swing.*;
public class Life {
public static void main(String[] args){
JFrame game = new JFrame("Life");
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setSize(500, 500);
MainPanel mainPanel = new MainPanel();
game.setContentPane(mainPanel);
game.setVisible(true);
}
}
Secondly, here’s my subclass of Jpanel:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainPanel extends JPanel implements ActionListener{
int i = 0;
int j = 0;
public MainPanel(){
super();
}
public void paintComponent(Graphics g){
j++;
g.drawLine(10,10, 20 + i, 20 + i);
Timer t = new Timer(1000, this);
t.start();
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
i++;
repaint();
}
}
Notice that the variable i is incremented each time actionPreformed is called and the variable j is called each time paintComponent is called. What winds up happening is that i starts to be much larger than j and the line drawn by paintComponent seems to grow at a faster and faster rate.
Here are my questions:
- Why does this happen?
- How can I sync things up so that the line gets redrawn each every 1000 ms?
- Given what I’m trying to do, is my approach wrong? Should I be doing things differently?
Thanks in advance.
Don’t start a Swing Timer from within a
paintComponentmethod.This method should do your painting, only your painting and nothing but painting. It should contain absolutely no program logic. Understand that you have very limited control over this method since you cannot predict when or if it will be called, how often it will be called. You can’t even call it yourself or be guaranteed that when you suggest it be called via
repaint()that it will in fact be called.Also this method must be fast, as fast possible since anything that slows it down, be it object creation or reading in files will reduce the perceived responsiveness of your GUI, the last thing that you want to see happen.
The solution is to separate the program logic out of that method and into better methods such as your constructor. Repeating code should be in a Swing Timer.
Edit:
You state:
You should never have code in paintComponent that takes that long or even 10’s of milliseconds. If there’s a risk of something like that happening, then do the drawing in a background thread and to a BufferedImage, and then on the Swing event thread show the BufferedImage in paintComponent method using the
Graphics#drawImage(...)method.