
本文旨在解决Java Swing应用中,点击按钮打开新JFrame时出现空白窗口或无法关闭的问题。核心原因在于使用while(true)循环阻塞了Swing的事件调度线程(EDT),以及JFrame实例化不当。教程将详细讲解如何通过javax.swing.Timer实现UI元素的周期性更新,并提供多JFrame之间切换的正确管理方法,确保UI响应流畅且组件正常显示。
在开发Java Swing桌面应用程序时,经常需要实现从一个窗口(JFrame)打开另一个窗口的功能。然而,开发者可能会遇到新打开的JFrame显示为空白、组件不显示,甚至无法关闭的情况。这通常是由于不当的线程管理和JFrame实例化方式导致的。本教程将深入探讨这些问题,并提供一套健壮的解决方案。
Swing应用程序是单线程的,所有与UI相关的操作都必须在事件调度线程(Event Dispatch Thread, EDT)上执行。如果任何耗时操作(如无限循环、长时间计算或Thread.sleep())在EDT上运行,就会阻塞UI,导致界面冻结、组件不响应或不显示。
原始代码中,setTime()方法包含一个while(true)循环和Thread.sleep(1000)。这个循环被直接调用,意味着它在EDT上无限运行,从而完全阻塞了UI更新,使得新JFrame的组件无法被绘制,也无法响应关闭事件。
立即学习“Java免费学习笔记(深入)”;
为了在Swing应用中实现周期性任务(如时钟更新),而又不阻塞EDT,正确的做法是使用javax.swing.Timer。Swing Timer会在EDT上触发事件,因此所有更新UI的代码都将安全地执行。
javax.swing.Timer 的基本用法如下:
import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
// ... 其他导入
public class MySwingApp extends JFrame {
private JLabel timeLabel;
private Timer clockTimer;
public MySwingApp() {
// ... JFrame初始化代码
timeLabel = new JLabel("00:00:00");
getContentPane().add(timeLabel);
// 创建一个每1000毫秒(1秒)触发一次的Timer
clockTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 在这里更新UI,例如更新时间标签
updateTimeLabel();
}
});
clockTimer.start(); // 启动Timer
}
private void updateTimeLabel() {
// 获取当前时间并更新timeLabel
// ...
}
// 当窗口关闭时,停止Timer以释放资源
private void stopTimer() {
if (clockTimer != null && clockTimer.isRunning()) {
clockTimer.stop();
}
}
// 示例:在窗口关闭时调用stopTimer
// this.addWindowListener(new WindowAdapter() {
// @Override
// public void windowClosing(WindowEvent e) {
// stopTimer();
// }
// });
}除了线程问题,原始代码中还存在一些JFrame管理上的问题:
以下是根据上述原则重构后的代码,解决了空白窗口、无法关闭以及时间更新不当的问题。
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer; // 导入Swing Timer
public class DisplayTimeDate extends JFrame {
private static final long serialVersionUID = 425524L;
private SimpleDateFormat timeFormat;
private SimpleDateFormat dayFormat;
private SimpleDateFormat dateFormat;
private JLabel timeLabel;
private JLabel dayLabel;
private JLabel dateLabel;
private JButton btnNewButton;
private Timer clockTimer; // 使用Swing Timer
public DisplayTimeDate() {
initializeForm();
setTime(); // 启动时间更新
}
private void initializeForm() {
// 当窗口关闭时停止Timer
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
if (clockTimer != null && clockTimer.isRunning()) {
clockTimer.stop();
}
// 如果是主窗口,可以选择退出应用
// System.exit(0);
}
});
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // 使用DISPOSE_ON_CLOSE
setTitle("我的时钟程序");
setAlwaysOnTop(true);
setSize(350, 200);
setResizable(false);
setLocationRelativeTo(null); // 窗口居中
timeFormat = new SimpleDateFormat("hh:mm:ss a");
dayFormat = new SimpleDateFormat("EEEE");
dateFormat = new SimpleDateFormat("MMMMM dd, yyyy");
timeLabel = new JLabel();
timeLabel.setFont(new Font("Verdana", Font.PLAIN, 50));
timeLabel.setForeground(new Color(0x00FF00));
timeLabel.setBackground(Color.black);
timeLabel.setOpaque(true);
dayLabel = new JLabel();
dayLabel.setFont(new Font("Ink Free", Font.PLAIN, 35));
dateLabel = new JLabel();
dateLabel.setFont(new Font("Ink Free", Font.PLAIN, 25));
getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
btnNewButton = new JButton("打开新窗口"); // 按钮文本更明确
btnNewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (clockTimer != null && clockTimer.isRunning()) {
clockTimer.stop(); // 停止当前窗口的Timer
}
dispose(); // 销毁当前窗口
// 在EDT上创建并显示新窗口
java.awt.EventQueue.invokeLater(() -> {
new TestTimeTake2().setVisible(true);
});
}
});
getContentPane().add(btnNewButton);
getContentPane().add(timeLabel);
getContentPane().add(dayLabel);
getContentPane().add(dateLabel);
}
public void setTime() {
// 创建一个每1000毫秒(1秒)触发一次的Swing Timer
clockTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
// 在Timer的actionPerformed方法中更新UI,确保在EDT上执行
String time = timeFormat.format(Calendar.getInstance().getTime());
timeLabel.setText(time);
String day = dayFormat.format(Calendar.getInstance().getTime());
dayLabel.setText(day);
String date = dateFormat.format(Calendar.getInstance().getTime());
dateLabel.setText(date);
}
});
clockTimer.start(); // 启动Timer
}
public static void main(String[] args) {
// 确保在EDT上创建和显示JFrame
java.awt.EventQueue.invokeLater(() -> {
new DisplayTimeDate().setVisible(true);
});
}
}import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer; // 导入Swing Timer
import javax.swing.border.EmptyBorder;
public class TestTimeTake2 extends JFrame {
private static final long serialVersionUID = 342241L;
private JPanel contentPane;
private SimpleDateFormat timeFormat;
private SimpleDateFormat dayFormat;
private SimpleDateFormat dateFormat;
private JLabel timeLabel;
private JLabel dayLabel;
private JLabel dateLabel;
private Timer clockTimer2; // 使用Swing Timer
public TestTimeTake2() {
initializeForm();
setTime(); // 启动时间更新
}
private void initializeForm() {
// 当窗口关闭时,停止Timer并重新打开DisplayTimeDate
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
if (clockTimer2 != null && clockTimer2.isRunning()) {
clockTimer2.stop(); // 停止当前窗口的Timer
}
// 在EDT上重新打开DisplayTimeDate窗口
java.awt.EventQueue.invokeLater(() -> {
new DisplayTimeDate().setVisible(true);
});
}
});
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(100, 100, 450, 300);
setLocationRelativeTo(null); // 窗口居中
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
timeFormat = new SimpleDateFormat("hh:mm:ss a");
dayFormat = new SimpleDateFormat("EEEE");
dateFormat = new SimpleDateFormat("dd-MMMMM-yyyy");
contentPane.setLayout(null); // 使用null布局,需要手动设置组件位置和大小
timeLabel = new JLabel();
timeLabel.setHorizontalAlignment(SwingConstants.CENTER);
timeLabel.setBounds(151, 45, 112, 14);
contentPane.add(timeLabel); // 添加到contentPane
dayLabel = new JLabel();
dayLabel.setHorizontalAlignment(SwingConstants.CENTER);
dayLabel.setBounds(151, 100, 112, 14);
contentPane.add(dayLabel); // 添加到contentPane
dateLabel = new JLabel();
dateLabel.setHorizontalAlignment(SwingConstants.CENTER);
dateLabel.setBounds(151, 151, 112, 14);
contentPane.add(dateLabel); // 添加到contentPane
}
public void setTime() {
// 创建一个每1000毫秒(1秒)触发一次的Swing Timer
clockTimer2 = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
// 在Timer的actionPerformed方法中更新UI
String time = timeFormat.format(Calendar.getInstance().getTime());
timeLabel.setText(time);
String day = dayFormat.format(Calendar.getInstance().getTime());
dayLabel.setText(day);
String date = dateFormat.format(Calendar.getInstance().getTime());
dateLabel.setText(date);
}
});
clockTimer2.start(); // 启动Timer
}
}通过遵循这些最佳实践,您可以构建出响应迅速、功能完善且易于维护的Java Swing应用程序,有效避免多窗口切换和UI更新中的常见问题。
以上就是Java Swing应用中多JFrame切换及时间更新的正确实践的详细内容,更多请关注php中文网其它相关文章!
Windows激活工具是正版认证的激活工具,永久激活,一键解决windows许可证即将过期。可激活win7系统、win8.1系统、win10系统、win11系统。下载后先看完视频激活教程,再进行操作,100%激活成功。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号