Java Swing 和 Thread 休眠
Java Swing and Thread sleeping
我决定做一些有趣的事情作为阅读编程书籍的休息时间,但我遇到了障碍。这是我的第一个挥杆项目,我已经准备好收尾了!
问题:我显然不明白线程如何与 Swing 一起工作。我正在为二十一点游戏编写 GUI,我首先完成了所有功能,例如当玩家中牌时在屏幕上绘制一张新牌,在玩家决定留下后显示庄家中牌,等等。这一切都有效。
当我添加逻辑来检查击中时是否有用户失败,或者当用户决定留下时谁赢了,游戏会立即进入 win/loss 屏幕,然后绘制:用户的卡片导致破产,或者;庄家开牌时抽到的牌(如果有的话)。
我尝试在不同的地方插入 Thread.sleep,但无济于事。程序会在抽牌之前休眠,然后像上面那样立即结束(即使它被逻辑上放置在抽牌调用之后,计算赢家的调用之前)。
我也试着在这里遵循 MVC 范式,仅供参考。
P.S。我的程序在一个线程上运行,我没有显式实例化另一个线程,但我依稀记得读过 Swing 生成它自己的图形线程
抱歉这么长的介绍!这是一些代码:
模型class'相关方法
void hit() {
//run when button is clicked
player.hand.add(deck.deck.get(0));
deck.deck.remove(0);
}
boolean isBust() {
if (player.getScore() > 21)
return true;
return false;
}
void dealerHit() {
while (dealer.getScore() < 17) { //could implement soft 17 rules for more difficulty
dealer.hand.add(deck.deck.get(0));
setChanged();
notifyObservers();
deck.deck.remove(0);
//Here was one attempt
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
boolean isWin() {
//and another
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ((player.getScore() > dealer.getScore() && player.getScore() <= 21) || dealer.getScore() > 21)
return true;
return false;
}
void stay() {
dealerHit();
isWin();
}
查看Class
void addHitListener(ActionListener HitListener) {
hit.addActionListener(HitListener);
}
void addStartListener(ActionListener StartListener) {
start.addActionListener(StartListener);
}
void addStayListener(ActionListener StayListener) {
stay.addActionListener(StayListener);
}
void display() {
JFrame myFrame = new JFrame("BlackJack");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setContentPane(this);
myFrame.setPreferredSize(new Dimension(700,550));
myFrame.pack();
myFrame.setVisible(true);
}
void addCards(Player p, Dealer d) {
topPanel.remove(start);
pcardPanel.add(playerlabel);
dcardPanel.add(dealerlabel);
for (Card c : p.hand) {
ImageIcon cc = new ImageIcon(c.img);
JLabel cC = new JLabel(cc);
//cC.setAlignmentX(alignmentX); use to get X alignment of card 1 & 2 for splits
//cC.setAlignmentY(alignmentY); same for Y, then increment by .3f
pcardPanel.add(cC);
}
for (Card c : d.hand)
dcardPanel.add(new JLabel(new ImageIcon(c.img)));
topPanel.add(new JLabel("Options: "));
topPanel.add(hit);
topPanel.add(stay);
validate();
repaint();
}
void endGame(boolean isWin) {
//I think I tried here, too
removeAll();
setBackground(new Color(0, 122, 0));
if (isWin == true)
add(new JLabel("You won!"));
else
add(new JLabel("You Lost"));
validate();
repaint();
}
public void hitPlayer(Player p) {
JLabel hits = new JLabel(new ImageIcon(p.hand.get(p.hand.size()-1).img));
//hits.setAlignmentY(alignmentY);
pcardPanel.add(hits);
validate();
repaint();
}
public void hitDealer(Dealer d) {
dcardPanel.add(new JLabel(new ImageIcon(d.hand.get(d.hand.size()-1).img)));
validate();
repaint();
}
控制器class:
public class Controller implements Observer {
BlackJack game;
Table t;
Controller(BlackJack game, Table t) {
this.game = game;
this.t = t;
this.game.addObserver(this);
this.t.addHitListener(new HitListener());
this.t.addStartListener(new StartListener());
this.t.addStayListener(new StayListener());
}
public void go() {
t.display();
}
public void update(Observable obj, Object observed) {
t.hitDealer(game.getDealer());
}
class HitListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
game.hit();
t.hitPlayer(game.getPlayer());
if (game.isBust() == true)
t.endGame(false);
}
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.addCards(game.getPlayer(), game.getDealer());
}
}
class StayListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
game.stay();
//doStay();
if (game.isWin() == true)
t.endGame(true);
else
t.endGame(false);
}
}
我只是有一个想法,因为我在 actionPerformed 方法中这样做,这可能就是为什么睡眠似乎影响 GUI 线程,而不是抽牌然后睡眠的原因。我敢打赌就是这样。但是我要去吃晚饭了,希望比我聪明的人能伸出援手!提前致谢
P.P.S。如果有任何拼写错误(我不认为有),只要知道它全部编译并工作!并且没有警告,如果有帮助的话
这对我来说阅读和理解有点太长了...但我想你的问题是因为你正在处理 Event Dispatching Thread
.
中的所有内容
当您在 GUI 上绘制一些东西然后进行更多处理时,只有当整个线程完成处理时,绘制才会真正反映在 GUI 上。这就是为什么您在 Thread.sleep
方法之前没有看到您的绘图。
相反,您应该使用 SwingWorker
将处理和 GUI 更新拆分到不同的线程。
看看这个https://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
与大多数 UI 一样,Swing 确实是一个单线程库。还有许多优化使其运行速度更快。举个例子——大多数绘画都被缓存并一起显示。即使不是这种情况,您也会依赖于系统的速度,这不是一个好主意。
如果你想要延迟动作,你需要use swing's timer(不要与其他计时器混淆class)。 class 有一个动作侦听器,它会在计时器到期时关闭。在你的情况下,你会检测到 win/bust 条件,启动计时器(例如在 2 秒内触发)并照常继续绘图。
我决定做一些有趣的事情作为阅读编程书籍的休息时间,但我遇到了障碍。这是我的第一个挥杆项目,我已经准备好收尾了!
问题:我显然不明白线程如何与 Swing 一起工作。我正在为二十一点游戏编写 GUI,我首先完成了所有功能,例如当玩家中牌时在屏幕上绘制一张新牌,在玩家决定留下后显示庄家中牌,等等。这一切都有效。
当我添加逻辑来检查击中时是否有用户失败,或者当用户决定留下时谁赢了,游戏会立即进入 win/loss 屏幕,然后绘制:用户的卡片导致破产,或者;庄家开牌时抽到的牌(如果有的话)。
我尝试在不同的地方插入 Thread.sleep,但无济于事。程序会在抽牌之前休眠,然后像上面那样立即结束(即使它被逻辑上放置在抽牌调用之后,计算赢家的调用之前)。
我也试着在这里遵循 MVC 范式,仅供参考。
P.S。我的程序在一个线程上运行,我没有显式实例化另一个线程,但我依稀记得读过 Swing 生成它自己的图形线程
抱歉这么长的介绍!这是一些代码:
模型class'相关方法
void hit() {
//run when button is clicked
player.hand.add(deck.deck.get(0));
deck.deck.remove(0);
}
boolean isBust() {
if (player.getScore() > 21)
return true;
return false;
}
void dealerHit() {
while (dealer.getScore() < 17) { //could implement soft 17 rules for more difficulty
dealer.hand.add(deck.deck.get(0));
setChanged();
notifyObservers();
deck.deck.remove(0);
//Here was one attempt
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
boolean isWin() {
//and another
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ((player.getScore() > dealer.getScore() && player.getScore() <= 21) || dealer.getScore() > 21)
return true;
return false;
}
void stay() {
dealerHit();
isWin();
}
查看Class
void addHitListener(ActionListener HitListener) {
hit.addActionListener(HitListener);
}
void addStartListener(ActionListener StartListener) {
start.addActionListener(StartListener);
}
void addStayListener(ActionListener StayListener) {
stay.addActionListener(StayListener);
}
void display() {
JFrame myFrame = new JFrame("BlackJack");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setContentPane(this);
myFrame.setPreferredSize(new Dimension(700,550));
myFrame.pack();
myFrame.setVisible(true);
}
void addCards(Player p, Dealer d) {
topPanel.remove(start);
pcardPanel.add(playerlabel);
dcardPanel.add(dealerlabel);
for (Card c : p.hand) {
ImageIcon cc = new ImageIcon(c.img);
JLabel cC = new JLabel(cc);
//cC.setAlignmentX(alignmentX); use to get X alignment of card 1 & 2 for splits
//cC.setAlignmentY(alignmentY); same for Y, then increment by .3f
pcardPanel.add(cC);
}
for (Card c : d.hand)
dcardPanel.add(new JLabel(new ImageIcon(c.img)));
topPanel.add(new JLabel("Options: "));
topPanel.add(hit);
topPanel.add(stay);
validate();
repaint();
}
void endGame(boolean isWin) {
//I think I tried here, too
removeAll();
setBackground(new Color(0, 122, 0));
if (isWin == true)
add(new JLabel("You won!"));
else
add(new JLabel("You Lost"));
validate();
repaint();
}
public void hitPlayer(Player p) {
JLabel hits = new JLabel(new ImageIcon(p.hand.get(p.hand.size()-1).img));
//hits.setAlignmentY(alignmentY);
pcardPanel.add(hits);
validate();
repaint();
}
public void hitDealer(Dealer d) {
dcardPanel.add(new JLabel(new ImageIcon(d.hand.get(d.hand.size()-1).img)));
validate();
repaint();
}
控制器class:
public class Controller implements Observer {
BlackJack game;
Table t;
Controller(BlackJack game, Table t) {
this.game = game;
this.t = t;
this.game.addObserver(this);
this.t.addHitListener(new HitListener());
this.t.addStartListener(new StartListener());
this.t.addStayListener(new StayListener());
}
public void go() {
t.display();
}
public void update(Observable obj, Object observed) {
t.hitDealer(game.getDealer());
}
class HitListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
game.hit();
t.hitPlayer(game.getPlayer());
if (game.isBust() == true)
t.endGame(false);
}
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
t.addCards(game.getPlayer(), game.getDealer());
}
}
class StayListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
game.stay();
//doStay();
if (game.isWin() == true)
t.endGame(true);
else
t.endGame(false);
}
}
我只是有一个想法,因为我在 actionPerformed 方法中这样做,这可能就是为什么睡眠似乎影响 GUI 线程,而不是抽牌然后睡眠的原因。我敢打赌就是这样。但是我要去吃晚饭了,希望比我聪明的人能伸出援手!提前致谢
P.P.S。如果有任何拼写错误(我不认为有),只要知道它全部编译并工作!并且没有警告,如果有帮助的话
这对我来说阅读和理解有点太长了...但我想你的问题是因为你正在处理 Event Dispatching Thread
.
当您在 GUI 上绘制一些东西然后进行更多处理时,只有当整个线程完成处理时,绘制才会真正反映在 GUI 上。这就是为什么您在 Thread.sleep
方法之前没有看到您的绘图。
相反,您应该使用 SwingWorker
将处理和 GUI 更新拆分到不同的线程。
看看这个https://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
与大多数 UI 一样,Swing 确实是一个单线程库。还有许多优化使其运行速度更快。举个例子——大多数绘画都被缓存并一起显示。即使不是这种情况,您也会依赖于系统的速度,这不是一个好主意。
如果你想要延迟动作,你需要use swing's timer(不要与其他计时器混淆class)。 class 有一个动作侦听器,它会在计时器到期时关闭。在你的情况下,你会检测到 win/bust 条件,启动计时器(例如在 2 秒内触发)并照常继续绘图。