Swing is a single thread environment. That is, all updates and interactions are executed within a single thread. Swing is also NOT thread safe. This means that all updates to the UI MUST be executed within the context of that thread (the Event Dispatching Thread or ETD).
Any code that blocks the EDT will prevent it from (amongst other things), repainting the UI and responding to input from the user.
You're paint code will NEVER update the screen, in fact it will make your application appear to "hang", as the paint
method isn't being allowed to complete and is blocking the ETD.
It is an exception that the paint
method will return quickly after been called and may be called repeatedly in quick succession.
Generally speaking, a Thread
is probably a little over kill, something like a javax.swing.Timer
would be more then suitable under these circumstances.

public class AnimatedBoat {
public static void main(String[] args) {
new AnimatedBoat();
}
public AnimatedBoat() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int xPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new File("boat.png"));
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
xPos += direction;
if (xPos + boat.getWidth() > getWidth()) {
xPos = getWidth() - boat.getWidth();
direction *= -1;
} else if (xPos < 0) {
xPos = 0;
direction *= -1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getWidth() * 4, boat.getHeight());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int y = getHeight() - boat.getHeight();
g.drawImage(boat, xPos, y, this);
}
}
}
As a side note. You should rarely need to override the paint
method of a top level container like JApplet
or JFrame
, while there are a number of good reasons for this, the one that you're going to most interested in is the fact that they're not double buffered, meaning you are likely to see flickering as the screen is updated.
It's better to use something like JPanel
and override it's paintComponent
method instead.
Take a look at
For more information
nb
While I've used a JFrame
for my example, it would be a simple matter to take the animation panel and put it into a JApplet
, this is another reasons why you don't need/want to extend from top level containers ;)