Это относительно распространенная проблема. Контейнеры верхнего уровня, такие как JFrame
, не имеют двойной буферизации. Это означает, что каждое изменение, внесенное в графический контекст, будет применено немедленно, что вызывает мерцание.
Как правило, вы не должны:
- Переопределять
paint
любого компонента - Рисование непосредственно в контейнеры верхнего уровня
JFrame
на самом деле является составным компонентом, то есть имеет несколько других дочерних компонентов, которые используются для генерации общего макета, и из-за того, как работает подсистема рисования, эти дочерние компоненты могут быть нарисованы независимо (или без уведомления) рамки.
Начните с чтения Выполнение пользовательской рисования и Рисование в AWT и Swing для получения более подробной информации о том, как рисование в Swing и как с ним работать.
В качестве общей рекомендации вам следует начать с JPanel
и переопределить его paintComponent
метод (не забудьте вызвать super.paintComponent
)
Помните, что Swing не является поточно-ориентированным, поэтому вы должны принять это во внимание ция. Вы можете использовать что-то вроде Swing Timer вместо Thread
, если частота кадров не является сверхкритической. Swing Timer
гарантирует, что все «тиковые» события происходят в потоке диспетчеризации событий, что делает его более безопасным для обновления пользовательского интерфейса (и состояния, от которого зависит пользовательский интерфейс).
Если вам требуется более прямой контроль , тогда вам нужно будет рассмотреть вопрос об использовании BufferStrategy
, который даст вам прямой контроль над тем, когда что-то нарисовано, что позволит вам лучше контролировать процесс рисования.
KeyListener
is как правило, плохой выбор для такого рода задач по ряду причин. Вместо этого вы должны использовать Key Bindings API , который решает эти недостатки.
Например ...
Это всего лишь простой пример рисования, с которого можно начать .. .
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Game {
public static void main(String[] args) {
new Game();
}
public Game() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Rectangle hBox;
private Rectangle vBox;
private int hDelta = 1;
private int vDelta = 1;
public TestPane() {
hBox = new Rectangle(0, 0, 10, 10);
vBox = new Rectangle(0, 0, 10, 10);
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
hBox.x += hDelta;
hBox.y = (getHeight() - 10) / 2;
if (hBox.x + hBox.width >= getWidth()) {
hBox.x = getWidth() - hBox.width;
hDelta *= -1;
} else if (hBox.x <= 0) {
hBox.x = 0;
hDelta *= -1;
}
vBox.y += vDelta;
vBox.x = (getWidth() - vBox.width) / 2;
if (vBox.y + vBox.height >= getHeight()) {
vBox.y = getHeight() - vBox.height;
vDelta *= -1;
} else if (vBox.y <= 0) {
vBox.y = 0;
vDelta *= -1;
}
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(hBox);
g2d.setColor(Color.BLUE);
g2d.fill(vBox);
g2d.dispose();
}
}
}