Java 禁用按钮时摇摆显示故障

Java Swing display glitch when disabling buttons

我正在处理的一个小程序出现了一个奇怪的故障。 scorekeeper/timer 适用于 Pictionary 或 Charades 等游戏。

我将从程序应该做什么开始。单击启动计时器按钮将禁用设置时间、重置计时器和启动计时器按钮,然后开始计时器倒计时。倒计时本身在线程中处理,因此可以使用 Timer Stop 按钮将其停止。所有这些功能都运行良好,除了当我单击“开始”时发生的奇怪的小图形故障:

您可以看到 'ghost' 的“重置计时器”按钮出现在数字上方,并且“6”的一小部分出现在“重置计时器”按钮上方。这通常是发生的情况,但有时它是出现的开始计时器的幽灵,或者有时根本什么都没有。 'ghost' 在倒计时循环的下一次迭代及其随后的计时器重绘中消失。

话虽这么说,我已将问题追溯到三个按钮的禁用。当我从我的代码中删除它时,没有出现任何故障。也许在禁用按钮后整个底部的 JPanel 需要重新绘制?我不认为那是必要的。无论如何,我将在下面粘贴相关代码。我希望你能提供一些关于如何防止这种情况发生的建议。

public void disableButtons() {
    timerStartButton.setEnabled(false);
    resetTimerButton.setEnabled(false);
    setTimeButton.setEnabled(false);
}

public void enableButtons() {
    timerStartButton.setEnabled(true);
    resetTimerButton.setEnabled(true);
    setTimeButton.setEnabled(true);
}

public void timerThread(){
    disableButtons();
    timerRunning = true;
    Thread t = new Thread(new Runnable(){
        @Override
        public void run(){
            timerInterrupted = false;
            while(timerRunning && timer > 0){
                try {
                    timerLabel.setText(Integer.toString(timer));
                    timerLabel.paintImmediately(timerLabel.getVisibleRect());
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(ScoreTime.class.getName()).log(Level.SEVERE, null, ex);
                }

                timer--;                
            }

            timerRunning = false;

            if (!timerInterrupted) {
                timer = 0;
                timerLabel.setText(Integer.toString(timer));
                if (soundOn) {
                    try {
                        //load the buzzer sound as a clip
                        AudioInputStream buzzerAudioIn = AudioSystem.getAudioInputStream(buzzerSoundFile);
                        Clip buzzerClip = AudioSystem.getClip();
                        buzzerClip.open(buzzerAudioIn);   
                        buzzerClip.start();
                        Thread.sleep(1500);
                    } catch (UnsupportedAudioFileException | IOException | LineUnavailableException | InterruptedException ex) {
                        Logger.getLogger(ScoreTime.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                else {
                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(ScoreTime.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                timer = userTime;
                timerLabel.setText(Integer.toString(timer));
                enableButtons();
            }
        }
    });
  t.start();
}

使用 Swing 计时器而不是单独的线程。

定时器将在您指定的时间间隔生成一个事件。代码将在 Event Dispatch Thread 上执行。然后你只需设置标签的文本。不需要 paintImmediately(...)。

Timer也支持stop/start方法,方便控制。

阅读 How to Use Swing Timers 上的 Swing 教程部分了解更多信息。

您还可以查看:Program freezes during Thread.sleep() and with Timer 一个简单的示例,每秒更新一次时间。