首先,对此啰嗦多久道歉。
我正在尝试制作一个简单的轮盘赌游戏,允许用户添加玩家,为这些玩家下注,并旋转轮盘赌轮,它表示为一个简单的JLabel,用它传递的每个数字更新它的文本。
但是,我遇到了一个我遇到很多麻烦的错误:JLabel只更新循环中 last 元素的文本。
基本上,我的解决方案是这样的:
当用户按下标有“Spin”的按钮时(假设用户已添加到游戏中),我从名为SpinWheelService
的类中调用一个方法,该类是一个Observable单例,后者又调用{ {1}}方法:
notifyObservers()
这是我的public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
String description = null;
if (ADD_PLAYER.equals(cmd)) {
addDialog();
} else if (PLACE_BET.equals(cmd)) {
betDialog();
} else if (SPIN.equals(cmd)) {
SpinWheelService.sws.setSpinWheelService();
} else if (DISPLAY.equals(cmd)) {
System.out.println("Display selected!");
}
}
课程:
SpinWheelService
为package model;
import java.util.*;
public class SpinWheelService extends Observable {
public static SpinWheelService sws = new SpinWheelService();
public void setSpinWheelService() {
setChanged();
notifyObservers();
}
}
注册的唯一监听器是这个类,其中GameEngine是我处理内部游戏逻辑的游戏引擎,WheelCallbackImpl是一个更新View的类:
SpinWheelService
这里要注意的主要问题是我的class SpinWheelObserver implements Observer {
GameEngine gameEngine;
ArrayList<SimplePlayer> players;
WheelCallbackImpl wheelCall;
int n;
public SpinWheelObserver(GameEngine engine, WheelCallbackImpl wheel, ArrayList<SimplePlayer> playerList) {
players = playerList;
gameEngine = engine;
wheelCall = wheel;
}
public void update(Observable sender, Object arg) {
// check if any players are present
if (players.size() == 0) {
System.out.println("Empty player array!");
return;
}
do {
gameEngine.spin(40, 1, 300, 30, wheelCall);
n = wheelCall.playback();
} while (n== 0);
}
}
方法,即:
gameEngine.spin()
方法callback.nextNumber(curNo,this):
public class GameEngineImpl implements GameEngine {
private List<Player> playerList = new ArrayList<Player>();
// method handles the slowing down of the roulette wheel, printing numbers at an incremental delay
public void delay(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
System.out.println("Sleep method failed.");
}
}
public void spin(int wheelSize, int initialDelay, int finalDelay,
int delayIncrement, WheelCallback callback) {
Random rand = new Random();
int curNo = rand.nextInt(wheelSize) + 1;
int finalNo = 0;
assert (curNo >= 1);
// while loop handles how long the wheel will spin for
while (initialDelay <= finalDelay) {
delay(initialDelay);
initialDelay += delayIncrement;
// handles rotating nature of the wheel, ensures that if it reaches wheel size, reverts to 1
if (curNo > wheelSize) {
curNo = 1;
callback.nextNumber(curNo, this);
curNo++;
}
assert (curNo <= wheelSize);
callback.nextNumber(curNo, this);
curNo++;
finalNo = curNo - 1;
}
calculateResult(finalNo);
callback.result(finalNo, this);
}
在哪里,wcWheel是我的View的单例实例,其中包含方法setCounter():
public void nextNumber(int nextNumber, GameEngine engine) {
String strNo = Integer.toString(nextNumber);
assert (nextNumber >= 1);
System.out.println(nextNumber);
wcWheel.setCounter(strNo);
}
对不起我的解释是多么复杂,但基本上归结为 public void setCounter(String value) {
label.setText(value);
}
肯定被调用,但似乎只在最终数字<上调用setCounter()
方法/ em>的。所以我留下的是一个空标签,在整个轮盘完成旋转之前不会显示数字。
我已经确定setText()
在事件调度线程上运行,我怀疑这是一个并发问题,但我不知道如何纠正它。
我试图包含所有相关代码,但如果我遗漏了任何内容,请提及它,我也会将其发布。
我的斗智尽头,所以如果有人能提供帮助,那就太棒了。
谢谢!
答案 0 :(得分:4)
while
循环Thread.sleep()
将阻止并重新创建或更改用户界面,直到循环结束。
相反,您希望为延迟实施javax.swing.Timer
,并保留计数器以确定停止数量。您可以在How to Use Swing Timers
基本构造是
Timer ( int delayInMillis, ActionListener listener )
其中delayInMillis
是点燃ActionEvent
之间的毫秒延迟。 listener
会听取此事件。因此,每次触发事件时,都会调用侦听器的actionPerfomed
。所以你可能会这样做:
Timer timer = new Timer(delay, new ActionListener()(
@Override
public void actionPerformed(ActionEvent e) {
if (count == 0) {
((Timer)e.getSource()).stop();
} else {
//make a change to your label
count--;
}
}
));
您可以致电timer.start()
启动计时器。每隔delay
毫秒,标签将更改为您需要的值,直到某个任意计数达到0,然后计时器停止。然后,您可以将计数变量设置为您需要的任何值,如果您想要随机,则可以根据车轮旋转的难度来确定:D
答案 1 :(得分:2)
我认为您没有发布确切知道问题所需的所有相关代码。
但很可能问题是由于您在EDT(事件调度线程)中运行循环和JLabel.setText()
。
请注意,更新UI组件(例如JLabel
的文本)也会在EDT中发生,因此当您的循环在EDT中运行时,文本将不会更新,只有在您的循环结束后才会更新从您的事件监听器返回。然后,由于您修改了JLabel
的文本,它将被刷新/重新绘制,您将看到您设置的最后一个值。
演示此内容的示例。在以下示例中,事件侦听器中的循环从0到9循环并设置标签的文本,但您只能看到最后的9个设置:< / p>
JPanel p = new JPanel();
final JLabel l = new JLabel("-1");
p.add(l);
JButton b = new JButton("Loop");
p.add(b);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for ( int i = 0; i < 10; i++ ) {
l.setText( "" + i );
try { Thread.sleep( 200 ); } catch ( InterruptedException e1 ) {}
}
}
} );
建议的解决方案:使用javax.swing.Timer
执行循环工作。 Swing的计时器在EDT中调用其监听器,因此可以安全地更新其中的swing组件,一旦监听器返回,组件UI更新可以立即发生:
JPanel p = new JPanel();
final JLabel l = new JLabel("-1");
p.add(l);
JButton b = new JButton("Loop");
p.add(b);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new Timer(200, new ActionListener() {
int i = 0;
@Override
public void actionPerformed(ActionEvent e2) {
l.setText("" + i);
if ( ++i == 10 )
((Timer)e2.getSource()).stop();
}
}).start();
}
} );
在此解决方案中,您将看到标签的文字从0到9很好地计数。
答案 2 :(得分:1)
在我看来,你的整个游戏必须在动作处理程序中阻塞,直到while
循环结束?因此标签的文本将会更新,但只有在AWT线程再次运行后才能看到最后一次更新。