无法重新绘制我的JFrame / JPanel

我创建了一个只在屏幕上移动球的程序。 我曾经把它全部放在一个class级,但我认为它看起来太乱了,所以我把它分成三个不同的类:Main …初始化一切,Game …画出一切,是一个JPanel,AL是一个KeyListener(也是问题所在)。 问题是,无论我尝试传递给它,我都无法从我的AL类重新编写程序。 有人能帮忙吗? 这是我的三个class级:

import java.awt.Color; import javax.swing.JFrame; public class Main { static Game game; static JFrame frame; public static void main(String[] args) { game = new Game(); frame = new JFrame(); frame.getContentPane().add(game); frame.addKeyListener(new AL(game, frame)); frame.setTitle("Game"); frame.setSize(500, 500); frame.setResizable(true); frame.setVisible(true); frame.setBackground(Color.BLACK); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } 

 import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import javax.swing.JFrame; import javax.swing.JPanel; public class Game extends JPanel implements Runnable { int x, y, xCoord, yCoord; private Image dbImage; private Graphics dbg; JFrame frame; public void changeCoord() { x += xCoord; y += yCoord; if (x = 480) { x = 480; } if (y = 480) { y = 480; } } public void setXCoord(int xcoord) { xCoord = xcoord; } public void setYCoord(int ycoord) { yCoord = ycoord; } public static void main(String[] args) { Game game = new Game(); Thread t = new Thread(game); t.start(); } public Game() { x = 250; y = 250; } @Override public void paintComponent(Graphics g) { g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15); } @Override public void paint(Graphics g) { dbImage = createImage(getWidth(), getHeight()); dbg = dbImage.getGraphics(); paintComponent(dbg); g.drawImage(dbImage, 0, 0, this); } @Override public void run() { try { while (true) { changeCoord(); Thread.sleep(30); } } catch (Exception e) { System.out.println(e.getMessage()); } } } 

 import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.JFrame; public class AL extends KeyAdapter { Game game; JFrame frame; public AL(Game game, JFrame frame) { this.game = game; this.frame = frame; } @Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); if (keyCode == e.VK_LEFT) { game.setXCoord(-1); } if (keyCode == e.VK_RIGHT) { game.setXCoord(+1); } if (keyCode == e.VK_UP) { game.setYCoord(-1); } if (keyCode == e.VK_DOWN) { game.setYCoord(+1); } game.repaint(); } @Override public void keyReleased(KeyEvent e) { int keyCode = e.getKeyCode(); if (keyCode == e.VK_LEFT) { game.setXCoord(0); } if (keyCode == e.VK_RIGHT) { game.setXCoord(0); } if (keyCode == e.VK_UP) { game.setYCoord(0); } if (keyCode == e.VK_DOWN) { game.setYCoord(0); } game.repaint(); } } 

让我们从明显的……开始吧

这有问题……

 @Override public void paintComponent(Graphics g) { g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15); } @Override public void paint(Graphics g) { dbImage = createImage(getWidth(), getHeight()); dbg = dbImage.getGraphics(); paintComponent(dbg); g.drawImage(dbImage, 0, 0, this); } 

没有必要在Swing组件中实现双缓冲,它们已经存在。 此外,你打破绘画合同,不要调用paint方法超级方法

整件事应该……

 @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15); } 

有关详细信息,请参阅AWT和Swing中的 绘画以及执行自定义绘画

众所周知KeyListener存在问题。 如果它注册的组件是可聚焦的并且具有键盘焦点,它将仅引发键事件。 默认情况下, JPanel不可调焦。 在你运行之前尝试让它变得可聚焦(并且让人感到非常失望)之前,你应该使用Key Bindings API ,这是为了KeyListener的局限性

作为一个基本的例子……

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Main { public static void main(String[] args) { new Main(); } public Main() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new Game()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class Game extends JPanel { int x, y, xCoord, yCoord; public Game() { x = 250; y = 250; addKeyBinding(KeyEvent.VK_LEFT, "move.left", new MoveAction(this, -1, 0)); addKeyBinding(KeyEvent.VK_RIGHT, "move.right", new MoveAction(this, 1, 0)); addKeyBinding(KeyEvent.VK_UP, "move.up", new MoveAction(this, 0, -1)); addKeyBinding(KeyEvent.VK_DOWN, "move.down", new MoveAction(this, 0, 1)); } protected void addKeyBinding(int keyCode, String name, Action action) { addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action); } protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = getActionMap(); inputMap.put(keyStroke, name); actionMap.put(name, action); } public void changeCoord() { x += xCoord; y += yCoord; if (x <= 20) { x = 20; } if (x >= 480) { x = 480; } if (y <= 40) { y = 40; } if (y >= 480) { y = 480; } repaint(); } public void setXCoord(int xcoord) { xCoord = xcoord; changeCoord(); } public void setYCoord(int ycoord) { yCoord = ycoord; changeCoord(); } @Override public Dimension getPreferredSize() { return new Dimension(480, 480); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15); } } public class MoveAction extends AbstractAction { private int xDelta; private int yDelta; // I'd prefer an interface with just the "move" methods, but // that's more time I don't have private Game game; public MoveAction(Game game, int xDelta, int yDelta) { this.xDelta = xDelta; this.yDelta = yDelta; this.game = game; } @Override public void actionPerformed(ActionEvent e) { game.setXCoord(xDelta); game.setYCoord(yDelta); } } } 

但是,等等,这不是你想要的(相信我,我是互联网上的一个小人物;)),一个更好的例子可能是……

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.HashSet; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Main { public static void main(String[] args) { new Main(); } public Main() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new Game()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public enum Direction { UP, LEFT, DOWN, RIGHT; } public class Game extends JPanel { int x, y, xCoord, yCoord; private Set movement; public Game() { x = 250; y = 250; movement = new HashSet<>(4); addKeyPressedBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(movement, Direction.LEFT, true)); addKeyReleasedBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(movement, Direction.LEFT, false)); addKeyPressedBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(movement, Direction.RIGHT, true)); addKeyReleasedBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(movement, Direction.RIGHT, false)); addKeyPressedBinding(KeyEvent.VK_UP, "up.pressed", new MoveAction(movement, Direction.UP, true)); addKeyReleasedBinding(KeyEvent.VK_UP, "up.released", new MoveAction(movement, Direction.UP, false)); addKeyPressedBinding(KeyEvent.VK_DOWN, "down.pressed", new MoveAction(movement, Direction.DOWN, true)); addKeyReleasedBinding(KeyEvent.VK_DOWN, "down.released", new MoveAction(movement, Direction.DOWN, false)); Timer timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { changeCoord(); } }); timer.start(); } protected void addKeyBinding(int keyCode, String name, Action action) { addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action); } protected void addKeyPressedBinding(int keyCode, String name, Action action) { addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action); } protected void addKeyReleasedBinding(int keyCode, String name, Action action) { addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action); } protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap actionMap = getActionMap(); inputMap.put(keyStroke, name); actionMap.put(name, action); } public void changeCoord() { if (movement.contains(Direction.UP)) { y--; } else if (movement.contains(Direction.DOWN)) { y++; } if (movement.contains(Direction.LEFT)) { x--; } else if (movement.contains(Direction.RIGHT)) { x++; } x += xCoord; y += yCoord; if (x <= 20) { x = 20; } if (x >= 480) { x = 480; } if (y <= 40) { y = 40; } if (y >= 480) { y = 480; } repaint(); } public void setXCoord(int xcoord) { xCoord = xcoord; changeCoord(); } public void setYCoord(int ycoord) { yCoord = ycoord; changeCoord(); } @Override public Dimension getPreferredSize() { return new Dimension(480, 480); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15); } } public class MoveAction extends AbstractAction { private Set movement; private Direction direction; private boolean pressed; public MoveAction(Set movement, Direction direction, boolean pressed) { this.movement = movement; this.direction = direction; this.pressed = pressed; } @Override public void actionPerformed(ActionEvent e) { if (pressed) { movement.add(direction); } else { movement.remove(direction); } } } } 

这样做只是在按下一个键时激活一个标志(当它被释放时停用它),然后在一个Swing Timer ,我们检查哪些键是“活动的”并更新球的位置。

这样做,消除了首次按下和按住按键时由操作系统引起的关键“断续”。 在第一个键和重复的键事件之间插入延迟。 相反,我们只是根据需要打开和关闭旗帜。

它还允许您同时向两个方向移动(水平和垂直)

查看Swing中的Concurrency以及如何使用Swing Timers获取更多详细信息