Swing — это однопоточная среда. То есть все обновления и взаимодействия выполняются в рамках одного потока. Swing также НЕ является потокобезопасным. Это означает, что все обновления пользовательского интерфейса ДОЛЖНЫ выполняться в контексте этого потока (потока диспетчеризации событий или ETD).
Любой код, который блокирует EDT, не позволит ему (среди прочего) перерисовывать пользовательский интерфейс и реагировать на ввод пользователя.
Ваш код рисования НИКОГДА не будет обновлять экран, на самом деле это заставит ваше приложение «зависнуть», так как метод paint
не может завершиться и блокирует ETD.
Исключением является то, что метод paint
быстро возвращается после вызова и может быть вызван повторно в быстрой последовательности.
Вообще говоря, Thread
, вероятно, слишком много, что-то вроде javax.swing.Timer
было бы более чем подходящим в этих обстоятельствах.
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);
}
}
}
В качестве примечания. Вам редко придется переопределять метод paint
контейнера верхнего уровня, такого как JApplet
или JFrame
, хотя для этого есть ряд веских причин, одна из которых вас больше всего интересует, это тот факт, что они не двойные буферизуется, что означает, что вы, вероятно, увидите мерцание при обновлении экрана.
Вместо этого лучше использовать что-то вроде JPanel
и переопределить его метод paintComponent
.
Взгляни на
Чтобы получить больше информации
nb Хотя я использовал JFrame
для своего примера, было бы просто взять панель анимации и поместить ее в JApplet
, это еще одна причина, по которой вам не нужно/не хочется расширяться из контейнеров верхнего уровня;)
person
MadProgrammer
schedule
22.01.2013