你说:
基本上,我希望我的代码在某个点阻塞并等待用户单击我的 JFrame 中的特定 JLabel。用户单击 JLabel 后,我希望在获取所单击的 JLabel 的 id 后恢复代码。我不知道我会怎么做。我之前的方式是在代码中的那个点,用户只需输入一个 id (和 Scanner 的 next() 方法块)。
您不想阻止您的程序(不是在事件驱动程序中),而是更改您的 GUI 的状态,并将您对用户输入的响应基于此状态。您尝试的解决方案是一种线性思维方式,不适用于 Swing 或其他事件驱动的 GUI。
例如,作为一个简单的例子,考虑给你的程序一个布尔变量,
private boolean labelClicked = false;
单击 JLabel 后,在 JLabel 的 MouseListener 中将其更改为 true。然后仅在布尔值为真时才响应其他用户输入。
或者另一种可能的解决方案:如果您想在按下 JLabel 后激活 JButton,则禁用 JButton,并从 JLabel 的 MouseListener 中调用 JButton 上的 setEnabled(true)。
关键是要思考事件驱动编程。
在这个论坛中问这样的问题时,另一个关键不是问我们一个特定于代码的问题,而是一个特定于行为的问题。这里真正的问题不是如何写入标准,而是如何让你的程序暂停并等待用户输入——这是一个很大的区别。
最后,如果您需要更具体和更好的建议,请向我们填写您的问题的详细信息,如果其中有任何令人困惑的地方,请务必提出问题。祝你好运!
编辑 1
您可能会有几个类,包括管理此应用程序的 GUI 和非 GUI 类,非 GUI 类可以包括:
- 播放器类:可以是人或计算机
- 棋盘类:棋盘的逻辑(非gui)表示
- Piece 类:所有具体 Pieces 派生自的抽象类
- ChessAI 课:这是你的 AI 课,它为计算机计算出下一个最佳棋步应该是什么。
- 游戏类:也许是本次讨论中最重要的类,因为它将控制游戏流程并决定轮到谁移动。
Game 类将包含一个名为 Player 的字段turn
,该字段将包含对轮到谁的引用。假设你的 Game 类包含两个 Player 变量,humanPlayer 和 computerPlayer。turn 变量将持有这些对象中的一个或另一个,而 Player 的响应方式将取决于 turn 持有的对象。
Game 类将有一个循环,一个游戏循环,它在告诉humanPlayer 对象和computerPlayer 对象轮到他们移动之间交替。
所以说轮到计算机了,计算下一个最佳动作需要一段时间。如果同时人类玩家试图在 GUI 上拖动棋子,GUI 将通知 Game 类人类试图移动,Game 类将检查是否轮到人类(通过检查if (turn == humanPlayer)
)以及是否轮到人类, 游戏会通知 GUI,GUI 会将棋子设置回原来的位置,并且可能会弹出警告消息。
在计算机请求 Game 移动其棋子后,Game 将告诉 GUI 移动计算机的棋子,然后 Game 将设置 turn = humanPlayer,现在允许人类玩家移动。在humanPlayer 尝试移动后,GUI 将告诉 Game 人类尝试移动。游戏将检查它是否有效,如果是,将告诉 computerPlayer 它现在应该采取行动。
请注意,当我说“告诉”时,我的意思是它将调用该对象的公共方法。
编辑2
我的意思是这样的:
public class Game {
private Player whitePlayer;
private Player blackPlayer;
private Player turn;
private Board board = new Board(); // non-GUI logical board
private Gui gui; // the Swing GUI that displays all
public Game(String humanPlayerName, boolean humanWhite) {
if (humanWhite) {
whitePlayer = new HumanPlayer(humanPlayerName, this);
blackPlayer = new ComputerPlayer("Computer", this);
} else {
whitePlayer = new ComputerPlayer("Computer", this);
blackPlayer = new HumanPlayer(humanPlayerName, this);
}
}
public void start() {
whitePlayer.setMyTurn(true); // tell white player to move
}
public void move(Player playerMakingMove, Move move) {
// only respond if the right player is making the move
if (turn == playerMakingMove) {
// check if its a valid move
// if so, tell GUI to make move
// check if game over
turn.setMyTurn(false); // current player's turn is over
turn = (turn == blackPlayer) ? whitePlayer : blackPlayer; // swap players
turn.setMyTurn(true); // tell other player, it's his turn
turn.makeMove(); // *** added
} else {
// send message that it's not their turn to move
}
}
}
因此,如果人类玩家尝试移动 GUI 将为人类玩家移动(...),但 Game 对象不会响应,除非实际上轮到人类了。
添加到 Player 的Edit 3
makeMove()
方法 请注意,Game 的move(...)
方法可能是所有需要的游戏循环,特别是如果 ComputerPlayer 的makeMove(...)
方法告诉 AI 引擎创建下一个最佳移动,然后再次调用 Game 的 move(...) 方法:
abstract class Player {
private String name;
private boolean myTurn = false;
protected Game game;
public Player(String name, Game game) {
this.name = name;
this.game = game;
}
public abstract void makeMove();
public boolean isMyTurn() {
return myTurn;
}
public void setMyTurn(boolean myTurn) {
this.myTurn = myTurn;
}
public String getName() {
return name;
}
}
class HumanPlayer extends Player {
public HumanPlayer(String name, Game game) {
super(name, game);
}
@Override
public void makeMove() {
// TODO: ask GUI to inform player that it's his turn to move and accept the move
}
}
class ComputerPlayer extends Player {
private ChessAi chessAi = new ChessAi();
public ComputerPlayer(String name, Game game) {
super(name, game);
}
@Override
public void makeMove() {
game.move(this, chessAi.calcBestMove());
}
}
两个玩家game.move(...)
都知道移动后会打电话,这使得游戏提示下一个玩家移动......直到游戏结束。
请注意,这不是一个正在运行的程序,也不是故意的,而是更多的模型来说明可能的游戏逻辑。我没有做过这样的事情,并且可能有更好和更清洁的方法来做到这一点,但我只想了解在事件驱动的 GUI 程序中实现基于回合的逻辑的方法。
编辑 4
编译并运行以下简单的事件驱动 GUI:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EventDrivenGui extends JPanel {
private static final Color GO_COLOR = Color.green;
private static final Color STOP_COLOR = Color.red;
private static final int SIDE = 300;
private static final int TIMER_DELAY = 4 * 1000; // change light every 4 seconds
private boolean go = false;
private JButton button = new JButton("Button");
public EventDrivenGui() {
add(button);
button.addActionListener(new ButtonListener());
new Timer(TIMER_DELAY, new TimerListener()).start();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color color = go ? GO_COLOR : STOP_COLOR;
g2.setColor(color);
g2.fillOval(0, SIDE / 3, SIDE, SIDE);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(SIDE, (4 * SIDE) / 3);
}
private class ButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent arg0) {
if (go) {
JOptionPane.showMessageDialog(EventDrivenGui.this, "Button is Active");
}
}
}
private class TimerListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// toggle go. if true, now false, and visa versa
go = !go;
repaint(); // redraw oval
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
EventDrivenGui mainPanel = new EventDrivenGui();
JFrame frame = new JFrame("EventDrivenGui");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
您会看到在 JPanel 中有一个 JButton。作为用户,您可以随时按下按钮,但它只会在灯为绿色时响应(当 go 布尔变量为 true 时)。这是一个示例,其中按钮的行为(是否响应按下)取决于类的状态,无论 go 是否为真。一旦设置了 GUI——构造函数被调用,它被放置在一个 JFrame 中,并被显示出来,没有代码阻塞或类似的东西。有一个 Swing Timer,它唯一的工作就是切换 go 的值并重新绘制 JFrame,但仅此而已。