从ActionListener调用JFrame显示异常,求Swing.Timer等解决方案
问题分析与解决方案
你的问题根源在于事件调度线程(EDT)被阻塞了:当你在ActionListener的actionPerformed方法里创建Progress对象时,这个方法本身是在EDT中执行的。而Progress构造函数里的t1.join()会让当前线程(也就是EDT)等待后台线程完成,这直接导致EDT无法处理窗口的绘制请求——直到后台线程跑完,EDT才会一次性刷新UI,所以你看到的是黑色窗口直接跳到进度条填满的状态。
下面给你两种常用的解决思路:
方案一:使用SwingWorker(推荐,适合带后台逻辑的进度更新)
SwingWorker是Swing专门为后台任务设计的工具类,它能在后台线程执行耗时操作,同时安全地在EDT中更新UI,完美适配这种进度条场景。
修改后的Progress类代码如下:
class Progress extends JFrame { JProgressBar progressBar; Progress() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 300, 300); JPanel main = new JPanel(); add(main); main.add(new JLabel("Loading...")); progressBar = new JProgressBar(0, 100); main.add(progressBar); // 创建SwingWorker处理后台任务 SwingWorker<Void, Integer> worker = new SwingWorker<>() { @Override protected Void doInBackground() throws Exception { // 后台执行耗时操作,这里模拟进度递增 for (int pro = 0; pro <= 100; pro++) { Thread.sleep(10); publish(pro); // 发布进度值,会触发process方法 } return null; } @Override protected void process(List<Integer> chunks) { // 在EDT中更新UI,chunks是publish传来的进度值列表 int latestProgress = chunks.get(chunks.size() - 1); progressBar.setValue(latestProgress); } @Override protected void done() { // 任务完成后的操作(可选) JOptionPane.showMessageDialog(Progress.this, "加载完成!"); } }; worker.execute(); // 启动后台任务 setVisible(true); } }
方案二:使用Swing Timer(适合简单的定时UI更新)
如果你的进度更新逻辑很简单(比如只是模拟递增),javax.swing.Timer更轻量——它的事件处理天然在EDT中执行,不需要手动处理线程安全问题。
修改后的Progress类代码如下:
class Progress extends JFrame { int pro = 0; JProgressBar progressBar; Progress() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 300, 300); JPanel main = new JPanel(); add(main); main.add(new JLabel("Loading...")); progressBar = new JProgressBar(0, 100); main.add(progressBar); // 创建Swing Timer,每隔10ms触发一次事件 Timer timer = new Timer(10, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (pro <= 100) { progressBar.setValue(pro); pro++; } else { ((Timer)e.getSource()).stop(); // 进度完成后停止定时器 JOptionPane.showMessageDialog(Progress.this, "加载完成!"); } } }); timer.start(); // 启动定时器 setVisible(true); } }
额外注意点
- 永远不要在EDT中执行耗时操作或调用
Thread.sleep()、join()这类阻塞方法,否则会导致UI卡顿甚至假死。 - 所有Swing组件的创建和更新操作,都应该在EDT中执行(上述两种方案都自动帮你处理了这一点)。
内容的提问来源于stack exchange,提问作者Sameer Gupta
相关产品推荐
相关产品推荐

