无法在 JComponent 中实现 keylistener
Unable to implement keylistener in JComponent
我已经在下面的代码上工作了很长一段时间,但我似乎无法让 keyListeners 工作。我试过移动 setFocusable(true)、requestFocus() 和 addKeyListener(this),但没有任何区别。
而且,在任何人提到它之前,是的,如果到目前为止我在所有阅读中都学到了一件事,那么互联网似乎一致认为按键绑定更优越。问题是,这是学校作业,所以我必须照章办事。我应该做些什么来激活 KeyListener?
public class SnakeGUI extends JComponent implements KeyListener {
private static JTextField timeKeeper;
private static JTextField scoreKeeper;
private static int time;
private static int score;
private static boolean playing;
private static SnakeSettings settings;
private static Snake snake;
private static SnakePanel snakePanel;
private static Timer gameTimer;
private static Timer moveTimer;
public static void main(String[] args) {
// @author Every second, the displayed time ticks up
TimerTask uptick = new TimerTask() {
public void run() {
time += 1;
scoreKeeper.setText(Integer.toString(score));
timeKeeper.setText(Integer.toString(time));
}
};
// @author Depending on difficulty, the snake moves at different speeds.
TimerTask move = new TimerTask() {
public void run() {
snake.move();
playing = ! snake.isGameOver(settings.getWidth(), settings.getHeight());
if (! playing) {
// @author Clear timers until next game.
gameTimer.cancel();
moveTimer.cancel();
}
else {
snakePanel.setDisplay(snake);
}
}
};
//@author Use defaults settings first time around.
settings = new SnakeSettings();
SnakeSettingsPanel settingsPanel;
JFrame jf;
SnakeGUI gui;
// @author Alternate between game/settings elements till they quit.
while (true) {
settingsPanel = new SnakeSettingsPanel(settings);
jf = new JFrame();
jf.add(settingsPanel);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Wait until they press play...
while (! settingsPanel.getPlay()) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
// @author Then, update settings accordingly.
settings = settingsPanel.getSettings();
gui = new SnakeGUI(settings);
// @author Remove settingspanel, add game gui.
jf.dispose();
jf = new JFrame();
jf.add(gui);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Begin a new game.
playing = true;
time = 0;
score = 0;
snake = new Snake(new Point(settings.getWidth()/2, settings.getHeight()/2));
gameTimer = new Timer();
moveTimer = new Timer();
gameTimer.schedule(uptick, 1000, 1000);
// @author Set the delay based on the game speed.
if (Speed.SLOW == settings.getSpeed()) {
moveTimer.schedule(move, 750, 750);
}
else if (Speed.MEDIUM == settings.getSpeed()) {
moveTimer.schedule(move, 500, 500);
}
else {
moveTimer.schedule(move, 333, 333);
}
// @author Wait until the game ends.
while (playing) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
jf.dispose();
}
}
public SnakeGUI(SnakeSettings set) {
addKeyListener(this);
setFocusable(true);
requestFocus();
setLayout(new GridBagLayout());
JLabel tm = new JLabel("Time:");
JLabel sc = new JLabel("Score:");
timeKeeper = new JTextField(4);
scoreKeeper = new JTextField(4);
timeKeeper.setText("0");
scoreKeeper.setText("0");
timeKeeper.setEditable(false);
scoreKeeper.setEditable(false);
JPanel jp = new JPanel();
jp.setLayout(new FlowLayout());
jp.add(tm);
jp.add(timeKeeper);
jp.add(sc);
jp.add(scoreKeeper);
snakePanel = new SnakePanel(set);
GridBagConstraints p = new GridBagConstraints();
p.gridx = 0;
p.gridy = 0;
add(jp, p);
p.gridy = 1;
add(snakePanel, p);
setVisible(true);
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
System.out.println(1); // For testing purposes
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
System.out.println(1);
snake.changeDirection(SnakeInterface.Direction.Left);
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
System.out.println(2); // For testing purposes
snake.changeDirection(SnakeInterface.Direction.Right);
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
snake.changeDirection(SnakeInterface.Direction.Down);
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
snake.changeDirection(SnakeInterface.Direction.Up);
}
}
}
好的,这个...
// @author Alternate between game/settings elements till they quit.
while (true) {
settingsPanel = new SnakeSettingsPanel(settings);
jf = new JFrame();
jf.add(settingsPanel);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Wait until they press play...
while (! settingsPanel.getPlay()) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
// @author Then, update settings accordingly.
settings = settingsPanel.getSettings();
gui = new SnakeGUI(settings);
// @author Remove settingspanel, add game gui.
jf.dispose();
jf = new JFrame();
jf.add(gui);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Begin a new game.
playing = true;
time = 0;
score = 0;
snake = new Snake(new Point(settings.getWidth()/2, settings.getHeight()/2));
gameTimer = new Timer();
moveTimer = new Timer();
gameTimer.schedule(uptick, 1000, 1000);
// @author Set the delay based on the game speed.
if (Speed.SLOW == settings.getSpeed()) {
moveTimer.schedule(move, 750, 750);
}
else if (Speed.MEDIUM == settings.getSpeed()) {
moveTimer.schedule(move, 500, 500);
}
else {
moveTimer.schedule(move, 333, 333);
}
// @author Wait until the game ends.
while (playing) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
jf.dispose();
}
太疯狂了,再加上 static
的使用,您很快就会 运行 陷入很多麻烦的领域,其中任何一个都可能很容易在您的脸上爆炸。
Swing 不是线程安全的,因此部分原因是线程之间存在脏状态风险(脏 read/writes),您冒着 UI 尝试在更新数据时执行绘制过程的风险 -尝试调试它。
Swing 是一个事件驱动的环境,也就是说,当某事发生并且您对其做出响应时,这与过程驱动的工作流不同,例如控制台应用程序,其中每个语句都紧随其后。
以下是一个过于简化的版本,它有一个“菜单”和“游戏”窗格,您可以使用 CardLayout
和一系列“观察者”
简单地在它们之间切换
一个“技巧”……实际上,这不是一个“技巧”,而是一个彻头彻尾的肮脏技巧,就是当“移动”计时器滴答作响时,我们再次请求焦点。同样,这是 hack,如果您在 UI 上有其他控件,您希望用户与之交互,那么这将给您带来无穷无尽的问题,正如我已经说过的,KeyListener
不是解决问题的合适方法。
您应该使用:
为了稍微不那么糟糕的做事方式...
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private CardLayout cardLayout;
private MenuPane menuPane;
private GamePane gamePane;
public MainPane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
menuPane = new MenuPane(new MenuPane.Observer() {
@Override
public void didStartGame(MenuPane source) {
gamePane.start();
cardLayout.show(MainPane.this, "game");
}
});
gamePane = new GamePane(new GamePane.Observer() {
@Override
public void gameDidEnd(GamePane source, int score) {
source.stop();
cardLayout.show(MainPane.this, "menu");
}
});
add(menuPane, "menu");
add(gamePane, "game");
}
}
public class MenuPane extends JPanel {
public interface Observer {
public void didStartGame(MenuPane source);
}
public MenuPane(Observer observer) {
setLayout(new GridBagLayout());
JPanel contentPane = new JPanel(new GridLayout(-1, 1));
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
observer.didStartGame(MenuPane.this);
}
});
contentPane.add(startButton);
add(contentPane);
}
}
public class GamePane extends JPanel {
public interface Observer {
public void gameDidEnd(GamePane source, int score);
}
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
private Set<Direction> keyManager = new TreeSet<>();
private Timer gameTimer;
private int score;
private Instant startedAt;
private Rectangle player = new Rectangle(195, 195, 10, 10);
public GamePane(Observer observer) {
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
keyManager.add(Direction.UP);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
keyManager.add(Direction.DOWN);
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
keyManager.add(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
keyManager.add(Direction.RIGHT);
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
keyManager.remove(Direction.UP);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
keyManager.remove(Direction.DOWN);
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
keyManager.remove(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
keyManager.remove(Direction.RIGHT);
}
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void start() {
stop();
keyManager.clear();
startedAt = Instant.now();
score = 0;
gameTimer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//scoreKeeper.setText(Integer.toString(score));
//timeKeeper.setText(Integer.toString(time));
move();
}
});
gameTimer.start();
}
public void stop() {
if (gameTimer != null) {
gameTimer.stop();
}
}
protected void move() {
requestFocusInWindow();
int delta = 1;
if (keyManager.contains(Direction.UP)) {
player.y -= delta;
}
if (keyManager.contains(Direction.DOWN)) {
player.y += delta;
}
if (keyManager.contains(Direction.LEFT)) {
player.x -= delta;
}
if (keyManager.contains(Direction.RIGHT)) {
player.x += delta;
}
if (player.y < 0) {
player.y = 0;
} else if (player.y + player.height >= getHeight()) {
player.y = getHeight() - player.height;
}
if (player.x < 0) {
player.x = 0;
} else if (player.x + player.width >= getWidth()) {
player.x = getWidth() - player.width;
}
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.setColor(Color.BLACK);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(score);
g2d.drawString(text, 16, fm.getAscent());
text = "---";
if (startedAt != null) {
text = Long.toString(Duration.between(startedAt, Instant.now()).toSeconds());
}
g2d.drawString(text, getWidth() - 16 - fm.stringWidth(text), fm.getAscent());
g2d.dispose();
}
}
}
哦,关于static
的话题,不要这样用,太容易出问题了。相反,使用依赖注入(即 Passing Information to a Method or a Constructor)
我已经在下面的代码上工作了很长一段时间,但我似乎无法让 keyListeners 工作。我试过移动 setFocusable(true)、requestFocus() 和 addKeyListener(this),但没有任何区别。
而且,在任何人提到它之前,是的,如果到目前为止我在所有阅读中都学到了一件事,那么互联网似乎一致认为按键绑定更优越。问题是,这是学校作业,所以我必须照章办事。我应该做些什么来激活 KeyListener?
public class SnakeGUI extends JComponent implements KeyListener {
private static JTextField timeKeeper;
private static JTextField scoreKeeper;
private static int time;
private static int score;
private static boolean playing;
private static SnakeSettings settings;
private static Snake snake;
private static SnakePanel snakePanel;
private static Timer gameTimer;
private static Timer moveTimer;
public static void main(String[] args) {
// @author Every second, the displayed time ticks up
TimerTask uptick = new TimerTask() {
public void run() {
time += 1;
scoreKeeper.setText(Integer.toString(score));
timeKeeper.setText(Integer.toString(time));
}
};
// @author Depending on difficulty, the snake moves at different speeds.
TimerTask move = new TimerTask() {
public void run() {
snake.move();
playing = ! snake.isGameOver(settings.getWidth(), settings.getHeight());
if (! playing) {
// @author Clear timers until next game.
gameTimer.cancel();
moveTimer.cancel();
}
else {
snakePanel.setDisplay(snake);
}
}
};
//@author Use defaults settings first time around.
settings = new SnakeSettings();
SnakeSettingsPanel settingsPanel;
JFrame jf;
SnakeGUI gui;
// @author Alternate between game/settings elements till they quit.
while (true) {
settingsPanel = new SnakeSettingsPanel(settings);
jf = new JFrame();
jf.add(settingsPanel);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Wait until they press play...
while (! settingsPanel.getPlay()) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
// @author Then, update settings accordingly.
settings = settingsPanel.getSettings();
gui = new SnakeGUI(settings);
// @author Remove settingspanel, add game gui.
jf.dispose();
jf = new JFrame();
jf.add(gui);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Begin a new game.
playing = true;
time = 0;
score = 0;
snake = new Snake(new Point(settings.getWidth()/2, settings.getHeight()/2));
gameTimer = new Timer();
moveTimer = new Timer();
gameTimer.schedule(uptick, 1000, 1000);
// @author Set the delay based on the game speed.
if (Speed.SLOW == settings.getSpeed()) {
moveTimer.schedule(move, 750, 750);
}
else if (Speed.MEDIUM == settings.getSpeed()) {
moveTimer.schedule(move, 500, 500);
}
else {
moveTimer.schedule(move, 333, 333);
}
// @author Wait until the game ends.
while (playing) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
jf.dispose();
}
}
public SnakeGUI(SnakeSettings set) {
addKeyListener(this);
setFocusable(true);
requestFocus();
setLayout(new GridBagLayout());
JLabel tm = new JLabel("Time:");
JLabel sc = new JLabel("Score:");
timeKeeper = new JTextField(4);
scoreKeeper = new JTextField(4);
timeKeeper.setText("0");
scoreKeeper.setText("0");
timeKeeper.setEditable(false);
scoreKeeper.setEditable(false);
JPanel jp = new JPanel();
jp.setLayout(new FlowLayout());
jp.add(tm);
jp.add(timeKeeper);
jp.add(sc);
jp.add(scoreKeeper);
snakePanel = new SnakePanel(set);
GridBagConstraints p = new GridBagConstraints();
p.gridx = 0;
p.gridy = 0;
add(jp, p);
p.gridy = 1;
add(snakePanel, p);
setVisible(true);
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
System.out.println(1); // For testing purposes
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
System.out.println(1);
snake.changeDirection(SnakeInterface.Direction.Left);
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
System.out.println(2); // For testing purposes
snake.changeDirection(SnakeInterface.Direction.Right);
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
snake.changeDirection(SnakeInterface.Direction.Down);
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
snake.changeDirection(SnakeInterface.Direction.Up);
}
}
}
好的,这个...
// @author Alternate between game/settings elements till they quit.
while (true) {
settingsPanel = new SnakeSettingsPanel(settings);
jf = new JFrame();
jf.add(settingsPanel);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Wait until they press play...
while (! settingsPanel.getPlay()) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
// @author Then, update settings accordingly.
settings = settingsPanel.getSettings();
gui = new SnakeGUI(settings);
// @author Remove settingspanel, add game gui.
jf.dispose();
jf = new JFrame();
jf.add(gui);
jf.pack();
jf.setTitle("Snake");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
// @author Begin a new game.
playing = true;
time = 0;
score = 0;
snake = new Snake(new Point(settings.getWidth()/2, settings.getHeight()/2));
gameTimer = new Timer();
moveTimer = new Timer();
gameTimer.schedule(uptick, 1000, 1000);
// @author Set the delay based on the game speed.
if (Speed.SLOW == settings.getSpeed()) {
moveTimer.schedule(move, 750, 750);
}
else if (Speed.MEDIUM == settings.getSpeed()) {
moveTimer.schedule(move, 500, 500);
}
else {
moveTimer.schedule(move, 333, 333);
}
// @author Wait until the game ends.
while (playing) {
try {
Thread.sleep(25);
} catch (Exception e) {
System.out.println(e);
}
}
jf.dispose();
}
太疯狂了,再加上 static
的使用,您很快就会 运行 陷入很多麻烦的领域,其中任何一个都可能很容易在您的脸上爆炸。
Swing 不是线程安全的,因此部分原因是线程之间存在脏状态风险(脏 read/writes),您冒着 UI 尝试在更新数据时执行绘制过程的风险 -尝试调试它。
Swing 是一个事件驱动的环境,也就是说,当某事发生并且您对其做出响应时,这与过程驱动的工作流不同,例如控制台应用程序,其中每个语句都紧随其后。
以下是一个过于简化的版本,它有一个“菜单”和“游戏”窗格,您可以使用 CardLayout
和一系列“观察者”
一个“技巧”……实际上,这不是一个“技巧”,而是一个彻头彻尾的肮脏技巧,就是当“移动”计时器滴答作响时,我们再次请求焦点。同样,这是 hack,如果您在 UI 上有其他控件,您希望用户与之交互,那么这将给您带来无穷无尽的问题,正如我已经说过的,KeyListener
不是解决问题的合适方法。
您应该使用:
为了稍微不那么糟糕的做事方式...
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private CardLayout cardLayout;
private MenuPane menuPane;
private GamePane gamePane;
public MainPane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
menuPane = new MenuPane(new MenuPane.Observer() {
@Override
public void didStartGame(MenuPane source) {
gamePane.start();
cardLayout.show(MainPane.this, "game");
}
});
gamePane = new GamePane(new GamePane.Observer() {
@Override
public void gameDidEnd(GamePane source, int score) {
source.stop();
cardLayout.show(MainPane.this, "menu");
}
});
add(menuPane, "menu");
add(gamePane, "game");
}
}
public class MenuPane extends JPanel {
public interface Observer {
public void didStartGame(MenuPane source);
}
public MenuPane(Observer observer) {
setLayout(new GridBagLayout());
JPanel contentPane = new JPanel(new GridLayout(-1, 1));
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
observer.didStartGame(MenuPane.this);
}
});
contentPane.add(startButton);
add(contentPane);
}
}
public class GamePane extends JPanel {
public interface Observer {
public void gameDidEnd(GamePane source, int score);
}
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
private Set<Direction> keyManager = new TreeSet<>();
private Timer gameTimer;
private int score;
private Instant startedAt;
private Rectangle player = new Rectangle(195, 195, 10, 10);
public GamePane(Observer observer) {
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
keyManager.add(Direction.UP);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
keyManager.add(Direction.DOWN);
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
keyManager.add(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
keyManager.add(Direction.RIGHT);
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
keyManager.remove(Direction.UP);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
keyManager.remove(Direction.DOWN);
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
keyManager.remove(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
keyManager.remove(Direction.RIGHT);
}
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void start() {
stop();
keyManager.clear();
startedAt = Instant.now();
score = 0;
gameTimer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//scoreKeeper.setText(Integer.toString(score));
//timeKeeper.setText(Integer.toString(time));
move();
}
});
gameTimer.start();
}
public void stop() {
if (gameTimer != null) {
gameTimer.stop();
}
}
protected void move() {
requestFocusInWindow();
int delta = 1;
if (keyManager.contains(Direction.UP)) {
player.y -= delta;
}
if (keyManager.contains(Direction.DOWN)) {
player.y += delta;
}
if (keyManager.contains(Direction.LEFT)) {
player.x -= delta;
}
if (keyManager.contains(Direction.RIGHT)) {
player.x += delta;
}
if (player.y < 0) {
player.y = 0;
} else if (player.y + player.height >= getHeight()) {
player.y = getHeight() - player.height;
}
if (player.x < 0) {
player.x = 0;
} else if (player.x + player.width >= getWidth()) {
player.x = getWidth() - player.width;
}
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.setColor(Color.BLACK);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(score);
g2d.drawString(text, 16, fm.getAscent());
text = "---";
if (startedAt != null) {
text = Long.toString(Duration.between(startedAt, Instant.now()).toSeconds());
}
g2d.drawString(text, getWidth() - 16 - fm.stringWidth(text), fm.getAscent());
g2d.dispose();
}
}
}
哦,关于static
的话题,不要这样用,太容易出问题了。相反,使用依赖注入(即 Passing Information to a Method or a Constructor)