让我们从显而易见的…开始。
这是有问题的…
@Overridepublic void paintComponent(Graphics g) { g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15);}@Overridepublic void paint(Graphics g) { dbImage = createImage(getWidth(), getHeight()); dbg = dbImage.getGraphics(); paintComponent(dbg); g.drawImage(dbImage, 0, 0, this);}现在已经不需要在Swing组件中实现双缓冲了。另外,通过不将paint方法称为super方法,您正在破坏绘画合同
整个事情应该是…
@Overrideprotected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.GREEN); g.fillOval(x, y, 15, 15);}有关更多详细信息,请参见AWT中的绘画和摇摆和执行自定义绘画。
KeyListener因存在问题而众所周知。仅当注册到的组件可操作并且具有键盘焦点时,它才会引发键事件。
JPanel默认情况下,A
不可聚焦。在尝试并使其具有针对性(让您非常失望)之前,您应该改用Key Bindings
API,该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<Direction> 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<Direction> movement; private Direction direction; private boolean pressed; public MoveAction(Set<Direction> 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中的并发性以及如何使用Swing计时器以了解更多详细信息



