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

如何让Swing JLabel实时显示Reciever类中更新的x值?

实时更新JLabel显示Reciever类中递增变量的解决方案

核心问题拆解

  • 目前仅在按钮点击时获取一次x值,没有建立对x持续变化的监听机制
  • Swing组件必须在**事件调度线程(EDT)**中更新,直接在TimerTask线程修改JLabel会触发线程安全问题

方案一:观察者模式(推荐解耦方案)

让Reciever在x更新时主动通知UI组件,实现数据变化的实时响应。

修改Reciever类

添加观察者管理逻辑,每次x递增后通知所有注册的监听者:

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class Reciever {
    private int x = 0;
    private Timer timer;
    private List<XChangeListener> listeners = new ArrayList<>();

    // 定义监听接口,规范数据更新回调
    public interface XChangeListener {
        void onXChanged(int newX);
    }

    // 注册监听者
    public void addXChangeListener(XChangeListener listener) {
        listeners.add(listener);
    }

    // 移除监听者
    public void removeXChangeListener(XChangeListener listener) {
        listeners.remove(listener);
    }

    // 启动定时更新任务
    public void start() {
        if (timer != null) return;
        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                x++;
                // 通知所有监听者x的变化
                notifyXChanged();
            }
        }, 0, 1000);
    }

    // 停止更新任务,避免内存泄漏
    public void stop() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

    private void notifyXChanged() {
        // 复制列表避免并发修改异常
        for (XChangeListener listener : new ArrayList<>(listeners)) {
            listener.onXChanged(x);
        }
    }
}

修改NGUI类

让GUI类实现监听接口,在回调中更新JLabel,同时用SwingUtilities.invokeLater确保在EDT线程执行:

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

public class NGUI extends JFrame implements Reciever.XChangeListener {
    private JLabel xLabel;
    private Reciever reciever;

    public NGUI() {
        initUI();
        reciever = new Reciever();
        reciever.addXChangeListener(this); // 注册为监听者
    }

    private void initUI() {
        setTitle("实时数据显示");
        setSize(300, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);

        xLabel = new JLabel("当前x值:0");
        xLabel.setBounds(50, 30, 200, 30);
        add(xLabel);

        JButton startBtn = new JButton("启动Reciever");
        startBtn.setBounds(50, 70, 200, 30);
        startBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                reciever.start();
            }
        });
        add(startBtn);
    }

    @Override
    public void onXChanged(int newX) {
        // 强制在Swing事件线程更新组件,避免线程安全问题
        SwingUtilities.invokeLater(() -> {
            xLabel.setText("当前x值:" + newX);
        });
    }

    public static void main(String[] args) {
        // 必须在EDT线程启动GUI
        SwingUtilities.invokeLater(() -> {
            new NGUI().setVisible(true);
        });
    }
}

方案二:使用SwingWorker(适合后台任务场景)

SwingWorker自带EDT线程支持,可直接处理后台任务和UI更新的衔接:

改写Reciever为SwingWorker

import javax.swing.SwingWorker;
import java.util.List;

public class RecieverWorker extends SwingWorker<Void, Integer> {
    private volatile boolean running = true;

    @Override
    protected Void doInBackground() throws Exception {
        int x = 0;
        while (running) {
            Thread.sleep(1000);
            x++;
            publish(x); // 把更新的数据发送到process方法
        }
        return null;
    }

    @Override
    protected void process(List<Integer> chunks) {
        // process方法自动在EDT线程执行,可直接更新UI
        int latestX = chunks.get(chunks.size() - 1);
        // 这里可直接对接UI更新逻辑
    }

    // 停止后台任务
    public void stop() {
        running = false;
        cancel(true);
    }
}

NGUI中使用SwingWorker

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

public class NGUI extends JFrame {
    private JLabel xLabel;
    private RecieverWorker worker;

    public NGUI() {
        initUI();
    }

    private void initUI() {
        setTitle("实时数据显示");
        setSize(300, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);

        xLabel = new JLabel("当前x值:0");
        xLabel.setBounds(50, 30, 200, 30);
        add(xLabel);

        JButton startBtn = new JButton("启动Reciever");
        startBtn.setBounds(50, 70, 200, 30);
        startBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (worker == null || worker.isDone()) {
                    worker = new RecieverWorker() {
                        @Override
                        protected void process(List<Integer> chunks) {
                            super.process(chunks);
                            xLabel.setText("当前x值:" + chunks.get(chunks.size() - 1));
                        }
                    };
                    worker.execute();
                }
            }
        });
        add(startBtn);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new NGUI().setVisible(true);
        });
    }
}

关键注意事项

  • 绝对不要在非EDT线程直接修改Swing组件,必须通过SwingUtilities.invokeLater或SwingWorker的process方法
  • 关闭窗口时记得调用Reciever的stop方法或SwingWorker的cancel方法,释放资源避免内存泄漏
  • 如果后续替换为读取真实文本文件,只需把TimerTask或SwingWorker中的x++逻辑替换为文件读取代码即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:10:17