Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Java Swing应用中多JFrame切换及时间更新的正确实践_创想鸟

Java Swing应用中多JFrame切换及时间更新的正确实践

java swing应用中多jframe切换及时间更新的正确实践

本文旨在解决Java Swing应用中,点击按钮打开新JFrame时出现空白窗口或无法关闭的问题。核心原因在于使用while(true)循环阻塞了Swing的事件调度线程(EDT),以及JFrame实例化不当。教程将详细讲解如何通过javax.swing.Timer实现UI元素的周期性更新,并提供多JFrame之间切换的正确管理方法,确保UI响应流畅且组件正常显示。

在开发Java Swing桌面应用程序时,经常需要实现从一个窗口(JFrame)打开另一个窗口的功能。然而,开发者可能会遇到新打开的JFrame显示为空白、组件不显示,甚至无法关闭的情况。这通常是由于不当的线程管理和JFrame实例化方式导致的。本教程将深入探讨这些问题,并提供一套健壮的解决方案。

理解Swing的事件调度线程(EDT)

Swing应用程序是单线程的,所有与UI相关的操作都必须在事件调度线程(Event Dispatch Thread, EDT)上执行。如果任何耗时操作(如无限循环、长时间计算或Thread.sleep())在EDT上运行,就会阻塞UI,导致界面冻结、组件不响应或不显示。

原始代码中,setTime()方法包含一个while(true)循环和Thread.sleep(1000)。这个循环被直接调用,意味着它在EDT上无限运行,从而完全阻塞了UI更新,使得新JFrame的组件无法被绘制,也无法响应关闭事件。

立即学习“Java免费学习笔记(深入)”;

核心解决方案:使用 javax.swing.Timer 进行周期性更新

为了在Swing应用中实现周期性任务(如时钟更新),而又不阻塞EDT,正确的做法是使用javax.swing.Timer。Swing Timer会在EDT上触发事件,因此所有更新UI的代码都将安全地执行。

Riffusion Riffusion

AI生成不同风格的音乐

Riffusion 87 查看详情 Riffusion

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管理与最佳实践

除了线程问题,原始代码中还存在一些JFrame管理上的问题:

重复实例化JFrame: 在testTime_take_2类中,它已经继承了JFrame,但在构造函数内部又声明并实例化了一个private static JFrame frame;。这是不必要的,并且可能导致混淆。应该直接使用this来指代当前类的JFrame实例。setDefaultCloseOperation 的选择:JFrame.EXIT_ON_CLOSE: 当此窗口关闭时,应用程序将退出。这通常只用于主应用程序窗口。JFrame.DISPOSE_ON_CLOSE: 当此窗口关闭时,它将被销毁并释放其资源,但应用程序不会退出。这适用于辅助窗口或子窗口,允许返回到之前的窗口。Swing UI创建和更新的线程安全: 始终使用SwingUtilities.invokeLater()或java.awt.EventQueue.invokeLater()来创建和显示Swing UI组件。这确保所有UI操作都在EDT上执行。类命名规范: Java类名应遵循PascalCase(驼峰命名法),例如TestTimeTake2而不是testTime_take_2。

示例代码重构

以下是根据上述原则重构后的代码,解决了空白窗口、无法关闭以及时间更新不当的问题。

DisplayTimeDate.java (主窗口)

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 Timerpublic 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);        });    }}

TestTimeTake2.java (新打开的窗口)

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 Timerimport 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    }}

注意事项与总结

EDT是关键: 任何对Swing组件的创建、修改或查询操作都必须在EDT上执行。使用javax.swing.Timer是实现周期性UI更新的黄金法则。setDefaultCloseOperation: 根据窗口的功能选择合适的关闭操作。对于非主窗口,JFrame.DISPOSE_ON_CLOSE是更安全的选项,它只会销毁当前窗口,而不会终止整个应用程序。资源管理: 当窗口关闭或不再需要时,及时停止相关的Timer,以避免资源泄漏和不必要的后台操作。WindowAdapter是一个很好的监听器来处理窗口关闭事件。布局管理器: 示例代码中TestTimeTake2使用了null布局(contentPane.setLayout(null);),这意味着所有组件的位置和大小都需要手动通过setBounds()方法设置。对于复杂的布局,建议使用Swing提供的各种布局管理器(如FlowLayout, BorderLayout, GridLayout, GridBagLayout等),它们能更好地适应不同屏幕尺寸和内容变化。避免重复实例化: 继承JFrame的类本身就是一个JFrame,无需在内部再创建一个JFrame实例。

通过遵循这些最佳实践,您可以构建出响应迅速、功能完善且易于维护的Java Swing应用程序,有效避免多窗口切换和UI更新中的常见问题。

以上就是Java Swing应用中多JFrame切换及时间更新的正确实践的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1064559.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
传音与腾讯合作,手机将搭载 GVoice 游戏语音技术
上一篇 2025年12月2日 05:56:08
Symantec、Thawte与DigiCert关系解析
下一篇 2025年12月2日 05:56:15

相关推荐

发表回复

登录后才能评论
关注微信