You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用setText方法实现计时器?JLabel更新失效问题排查求助

问题分析与解决方案

嘿,这个问题我之前踩过坑!核心原因是你阻塞了Swing的事件调度线程(EDT),而且没有遵循Swing的UI更新规则,才导致了UI卡顿和只显示最后一个数值的问题。咱们一步步拆解:

为什么会出现这个问题?

  1. EDT被阻塞:Swing的所有UI事件(比如按钮点击)和UI更新都依赖事件调度线程(EDT)执行。你的setTimer()方法是在actionPerformed里调用的,而actionPerformed本身就在EDT上运行。你用while(true)循环加Thread.sleep(1000)直接卡死了EDT——它根本没时间去更新JLabel的显示,直到整个循环结束才会一次性刷新最后一次的setText结果,所以你只能看到最后一个数值,按钮也会全程卡顿。
  2. 控制台输出不受影响:System.out.println(i)能正常输出,是因为它运行在控制台的独立线程,和EDT互不干扰。

正确的解决方案:使用Swing Timer

Swing专门提供了javax.swing.Timer来处理定时UI更新任务,它不会阻塞EDT,而且自动在EDT上执行UI更新操作,完美适配你的需求。下面是修改后的完整代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.border.EtchedBorder;

public class Game extends JFrame implements ActionListener {
    JLabel jTimer = new JLabel();//Showing time
    JLabel jScore = new JLabel("");//Showing score
    // 新增Swing Timer和计数变量
    private Timer swingTimer;
    private int timerCount = 0;

    Game() {
        JFrame Gframe = new JFrame("Game play");
        Gframe.setBounds(400, 400, 800, 800);
        Gframe.setLayout(null);
        JPanel pScore = new JPanel();
        EtchedBorder border = new EtchedBorder();
        JLabel score = new JLabel(" Score ");
        Font font1 = new Font("굴림", Font.PLAIN, 20);
        jScore.setPreferredSize(new Dimension(5, 5));
        score.setFont(font1);
        score.setBounds(330, 30, 100, 100);
        score.setBorder(border);
        Font font2 = new Font("고딕체", Font.PLAIN, 20);
        JLabel ttime = new JLabel(" Time ");
        JButton start = new JButton("START");
        start.setFont(font2);
        start.setBounds(150, 40, 100, 100);
        start.setBorder(border);
        start.addActionListener(this);
        jTimer.setLayout(null);
        jTimer.setBounds(330, 30, 300, 300);
        ttime.setFont(font2);
        ttime.setBounds(200, 15, 100, 100);
        ttime.setBorder(border);
        pScore.setLayout(new GridLayout(2, 2));
        pScore.setBounds(330, 30, 200, 100);
        pScore.add(score);
        pScore.add(ttime);
        pScore.add(jScore);
        pScore.add(jTimer);
        add(start);
        Gframe.add(pScore);//Including Score&Time
        Gframe.add(start);//Adding Start Button
        Gframe.setVisible(true);
    }

    // 重写计时方法,用Swing Timer实现
    public void setTimer() {
        timerCount = 0;
        // 初始化Timer:每1000ms触发一次,触发时执行UI更新
        swingTimer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (timerCount < 60) {
                    jTimer.setText(timerCount + "Sec");
                    System.out.println(timerCount);
                    timerCount++;
                } else {
                    // 计时结束,停止Timer
                    swingTimer.stop();
                }
            }
        });
        swingTimer.start();
    }

    public void actionPerformed(ActionEvent e) {
        String str = e.getActionCommand();
        if (str.equals("START")) {
            System.out.println("Counting");
            setTimer();
        }
    }

    // 标准Swing程序启动方式:在EDT初始化UI
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Game());
    }
}

关键修改点说明

  • 新增swingTimer和timerCount变量:分别管理定时任务和计时数值
  • 替换while循环为Swing Timer:它会在后台调度定时任务,每次触发时自动在EDT上执行UI更新,完全不会阻塞UI
  • 计时到60秒时停止Timer:避免无限触发
  • 添加main方法并使用SwingUtilities.invokeLater:这是Swing程序的标准启动方式,确保UI组件在EDT上初始化,避免潜在的线程安全问题

额外注意事项

  • 永远不要在EDT上执行长时间运行的任务(比如循环、sleep、IO操作),否则必现UI卡顿
  • Swing的所有UI更新操作必须在EDT上执行,Swing Timer帮你自动做到了这一点
  • 你的代码里用了setLayout(null)手动布局组件,虽然能快速定位,但推荐使用Swing内置的布局管理器(比如BorderLayout、GridLayout),这样窗口大小变化时组件能自动调整,代码更易维护

内容的提问来源于stack exchange,提问作者Akses

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:38:28