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

Java analogue clock首次显示正常但后续不更新且冻结问题求助

解决Java简易模拟时钟不更新、界面冻结的问题

看起来你遇到的是Swing UI开发里很常见的坑——没有用EDT安全的定时机制触发重绘,或者不小心阻塞了事件调度线程(EDT),导致时钟只显示初始时间,界面彻底卡住。我来给你拆解问题并给出修复方案:

问题根源分析

  • 缺少定时重绘触发:如果你的代码只在初始化时绘制了一次时钟,没有定时更新时间并通知UI重绘,时间自然不会变化。
  • EDT被阻塞:如果你尝试用java.util.Timer或者直接在主线程里写while(true)循环更新时间,会直接阻塞EDT——这个线程是Swing处理所有UI事件和绘制操作的核心线程,一旦被阻塞,界面就会彻底冻结,连刷新都做不了。

修复方案:用Swing Timer实现EDT安全的定时更新

Swing提供了专门的javax.swing.Timer,它是EDT友好的,所有事件回调都会在EDT中执行,不会阻塞UI。我们需要做这几件事:

  • 分离「时间更新」和「UI绘制」的逻辑
  • 用Timer每秒触发一次时间更新+重绘
  • 确保所有UI操作都在EDT中执行

下面是修复后的完整代码示例:

import javax.swing.*;
import java.awt.*;
import java.awt.BasicStroke;
import java.util.Calendar;

public class AnalogueClock extends JPanel {
    private int hours, minutes, seconds;

    public AnalogueClock() {
        setBackground(Color.WHITE);
        // 初始化当前时间
        updateCurrentTime();
        // 创建每秒触发一次的Swing Timer
        Timer clockTimer = new Timer(1000, event -> {
            updateCurrentTime();
            repaint(); // 通知面板重新绘制
        });
        clockTimer.start();
    }

    // 专门负责更新时间数据,不涉及UI操作
    private void updateCurrentTime() {
        Calendar now = Calendar.getInstance();
        hours = now.get(Calendar.HOUR_OF_DAY) % 12; // 转12小时制
        minutes = now.get(Calendar.MINUTE);
        seconds = now.get(Calendar.SECOND);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        // 开启抗锯齿,让线条更平滑
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        int clockRadius = Math.min(centerX, centerY) - 10;

        // 绘制时钟外框
        g2d.setStroke(new BasicStroke(2));
        g2d.drawOval(centerX - clockRadius, centerY - clockRadius, clockRadius * 2, clockRadius * 2);

        // 绘制时针、分针、秒针
        drawClockHand(g2d, centerX, centerY, clockRadius * 0.5, hours * 30 + minutes * 0.5, new BasicStroke(3));
        drawClockHand(g2d, centerX, centerY, clockRadius * 0.75, minutes * 6, new BasicStroke(2));
        drawClockHand(g2d, centerX, centerY, clockRadius * 0.9, seconds * 6, new BasicStroke(1));

        // 绘制中心圆点
        g2d.fillOval(centerX - 5, centerY - 5, 10, 10);
    }

    // 封装绘制指针的逻辑
    private void drawClockHand(Graphics2D g2d, int x, int y, double length, double angle, BasicStroke stroke) {
        g2d.setStroke(stroke);
        // 转换角度:Swing的0度在右侧,我们需要让0度在顶部,所以减90度
        double radian = Math.toRadians(angle - 90);
        int endX = (int) (x + length * Math.cos(radian));
        int endY = (int) (y + length * Math.sin(radian));
        g2d.drawLine(x, y, endX, endY);
    }

    public static void main(String[] args) {
        // 确保UI在EDT中启动,这是Swing的强制规范
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("简易模拟时钟");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new AnalogueClock());
            frame.setSize(400, 400);
            frame.setLocationRelativeTo(null); // 窗口居中
            frame.setVisible(true);
        });
    }
}

关键注意点

  • 必须用javax.swing.Timer:不要用java.util.Timer,后者的回调不在EDT中,直接操作UI会导致线程安全问题,甚至界面卡顿。
  • EDT安全原则:所有创建、更新Swing组件的操作都要放在SwingUtilities.invokeLater()里,比如main方法中的窗口初始化。
  • 职责分离:paintComponent只负责绘制,不要在里面做时间更新这类逻辑——绘制方法应该只根据当前数据渲染UI,避免耗时操作阻塞EDT。

如果你的原始代码里有类似while(true)的循环,一定要删掉,换成Swing Timer来实现定时任务,这样界面就能正常刷新,时间也会实时更新了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:32:08