java - JLabel.setText() 仅为循环中的最后一个元素设置文本

标签 java swing user-interface awt event-dispatch-thread

首先,对于这么长的篇幅,我深表歉意。

我正在尝试制作一个简单的轮盘游戏,它允许用户添加玩家、为这些玩家下注并旋转轮盘,它表示为一个简单的 JLabel,它会根据它通过的每个数字更新它的文本。

但是,我遇到了一个让我很头疼的错误:JLabel 只更新循环中last 元素的文本。

基本上,我的解决方案是这样工作的:

当用户按下标有“旋转”的按钮时(假定用户已添加到游戏中),我从名为 SpinWheelService 的类中调用一个方法,该类是一个 Observable 单例调用 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();
    }

}

SpinWheelService 注册的唯一监听器是此类,其中 GameEngine 是我处理内部游戏逻辑的游戏引擎,WheelCallbackImpl 是更新 View 的类:

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() 方法,它是这样的:

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);

}

方法回调.nextNumber(curNo, this):

public void nextNumber(int nextNumber, GameEngine engine) {

    String strNo = Integer.toString(nextNumber);

    assert (nextNumber >= 1);

    System.out.println(nextNumber);

    wcWheel.setCounter(strNo);
}

其中,wcWheel 是我的 View 的单例实例,其中包含方法 setCounter():

 public void setCounter(String value) {
    label.setText(value);
}

很抱歉我的解释很复杂,但基本上可以归结为 setCounter() 确实被调用了,但似乎只调用了 setText() 最终数字的方法。所以我剩下的是一个空标签,在整个轮盘旋转完成之前不会显示数字。

我已经确定 setCounter() 在事件派发线程上运行,我怀疑这是一个并发问题,但我不知道如何更正它。

我已尝试包含所有相关代码,但如果我遗漏任何内容,请提及,我也会将其发布。

我已经无计可施了,所以如果有人愿意提供帮助,那就太好了。

谢谢!

最佳答案

沿着 Thread.sleep()while 循环将阻塞并重新绘制或更改 UI,直到循环完成。

相反,您需要为延迟实现一个 javax.swing.Timer,并为滴答数保留一个计数器,以停止它。您可以在 How to Use Swing Timers 查看更多信息

基本结构是

Timer ( int delayInMillis, ActionListener listener )

其中 delayInMillisActionEvent 触发之间的毫秒延迟。此事件由 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,然后计时器停止。然后,您可以将 count 变量设置为您需要的任何值,如果您想要随机的,请根据轮子旋转的硬度说:D

关于java - JLabel.setText() 仅为循环中的最后一个元素设置文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23538005/

相关文章:

objective-c - 在桌面上显示进度栏(透明窗口) - Cocoa

java - jpql错误在哪里附近

java - 更新: No Resource Found后android studio中的资源错误

Java Swing 定时器

java - 根据用户输入集查询数据库 - JAVA

java - 在 JTable 中禁用单列拖动

python - 如何允许 Tkinter 从列表输入生成列表框

java - Android Studio 无法找到有效的 Jvm(与 MAC OS 相关)

java - 为什么不推荐错误处理?

java - 如何解决JTree中的显示问题?