在程序运行时保持 JPanel 中的计时器处于活动状态



我在一个名为 MainFrame 的类中创建了一个 GUI。GUI 的一个 JPanels 按秒显示当前时间和日期。当用户决定使用 GUI 分析数据时,它会调用处理数据的类。当数据处理发生时,计时器暂停,然后在数据处理结束时恢复。即使程序正在运行,如何让计时器连续运行?计时器是它自己的线程,但我不明白从哪里开始 JPanel 的线程。 以下是一些代码切口

应用程序.java(用于启动整个 GUI 的应用程序)

public class App {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MainFrame();
}
});
}
}

大型机(处理 JPanels 和数据处理 impl 的类)

public class MainFrame extends JFrame {
private DataProcess dataProcess = null;
...
...
private StatusPanel statusPanel;
...
...
public MainFrame() {
...
setJMenuBar(createFrameMenu());
initializeVariables();
constructLayout();
createFileChooser();
constructAppWindow();
}
private void initializeVariables() {
this.dataProcess = new DataProcess();
...
this.statusPanel = new StatusPanel();
...
}
private void constructLayout() {
JPanel layoutPanel = new JPanel();
layoutPanel.setLayout(new GridLayout(0, 3));
layoutPanel.add(dataControlsPanel());
setLayout(new BorderLayout());
add(layoutPanel, BorderLayout.CENTER);
add(statusPanel, BorderLayout.PAGE_END);
}

状态面板(显示计时器等的面板)

public class StatusPanel extends JPanel {
private static final long serialVersionUID = 1L;
private JLabel statusLabel;
private JLabel timeLabel;
private Timer timer;
public StatusPanel() {
initializeVariables();
constructLayout();
startTimer();
}
private void constructLayout() {
setLayout(new FlowLayout(FlowLayout.CENTER));
add(statusLabel);// , FlowLayout.CENTER
add(timeLabel);
}
public void startTimer() {
this.timer.start();
}
public void stopTimer() {
this.timer.setRunning(false);
}
private void initializeVariables() {
this.statusLabel = new JLabel();
this.timeLabel = new JLabel();
this.statusLabel.setText(StringConstants.STATUS_PANEL_TEXT);
this.timer = new Timer(timeLabel);
}
}

计时器.java(在状态面板中使用的计时器)

public class Timer extends Thread {
private boolean isRunning;
private JLabel timeLabel;
private SimpleDateFormat timeFormat;
public Timer(JLabel timeLabel) {
initializeVariables(timeLabel);
}
private void initializeVariables(JLabel timeLabel) {
this.timeLabel = timeLabel;
this.timeFormat = new SimpleDateFormat("HH:mm:ss  dd-MM-yyyy");
this.isRunning = true;
}
@Override
public void run() {
while (isRunning) {
Calendar calendar = Calendar.getInstance();
Date currentTime = calendar.getTime();
timeLabel.setText(timeFormat.format(currentTime));
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
}

数据处理是在 dataControlsPanel 中使用操作侦听器完成的。

当用户决定使用 GUI 分析数据时,它会调用处理数据的类。当数据处理发生时,计时器暂停,然后在数据处理结束时恢复。即使程序正在运行,如何让计时器连续运行

首先,您的计时器应该是javax.swing.Timer或"摆动"计时器。这是专门为 Swing 事件线程而构建的,因此应该避免当前代码显示的许多 Swing 线程问题 - 例如,此处:timeLabel.setText(timeFormat.format(currentTime));- 这会从后台线程进行 Swing 调用,并且是危险的代码。下一个 处理代码应该进入 SwingWorker。当工作线程执行时,您可以通过调用计时器上的stop()来暂停摆动计时器,或者只是让计时器继续运行。当 SwingWorker 完成其操作时 - 我通常使用添加到 SwingWorker 的 PropertyChangeListener 来侦听它,侦听其state属性更改为SwingWorker.StateValue.DONE,调用 worker 上的get()以提取它持有的任何数据,更重要的是捕获可能引发的任何异常。

例如:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
@SuppressWarnings("serial")
public class MyApp extends JPanel {
// display the date/time
private static final String DATE_FORMAT = "HH:mm:ss  dd-MM-yyyy";
private static final DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
// timer updates measures seconds, but updates every 0.2 sec's to be sure
private static final int TIMER_DELAY = 200;
// JLabel that shows the date/time
private JLabel timeLabel = new JLabel("", SwingConstants.CENTER);
// JButton's Action / listener. This starts long-running data processing
private Action dataProcessAction = new DataProcessAction("Process Data");
// the SwingWorker that the above Action executes:
private LongRunningSwProcess longRunningProcess;
// label to display the count coming from the process above
private JLabel countLabel = new JLabel("00");
public MyApp() {
// create a simple GUI
JPanel dataProcessingPanel = new JPanel();
dataProcessingPanel.add(new JButton(dataProcessAction)); // button that starts process
dataProcessingPanel.add(new JLabel("Count:"));
dataProcessingPanel.add(countLabel);        
setLayout(new BorderLayout());
add(timeLabel, BorderLayout.PAGE_START);
add(dataProcessingPanel);
showTimeLabelCurrentTime();
// create and start Swing Timer
new Timer(TIMER_DELAY, new TimerListener()).start(); 
}
// display count from swing worker
public void setCount(int newValue) {
countLabel.setText(String.format("%02d", newValue));
}
// clean up code after SwingWorker finishes
public void longRunningProcessDone() {
// re-enable JButton's action
dataProcessAction.setEnabled(true);
if (longRunningProcess != null) {
try {
// handle any exceptions that might get thrown from the SW
longRunningProcess.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
// display the current time in our timeLabel JLabel
private void showTimeLabelCurrentTime() {
long currentTime = System.currentTimeMillis();
Date date = new Date(currentTime);
timeLabel.setText(dateFormat.format(date));
}
// Timer's ActionListener is simple -- display the current time in the timeLabel
private class TimerListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
showTimeLabelCurrentTime();
}
}
// JButton's action. This starts the long-running SwingWorker
private class DataProcessAction extends AbstractAction {
public DataProcessAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
@Override
public void actionPerformed(ActionEvent e) {
setEnabled(false); // first disable the button's action
countLabel.setText("00"); // reset count label
// then create SwingWorker and listen to its changes
longRunningProcess = new LongRunningSwProcess();
longRunningProcess.addPropertyChangeListener(new DataProcessListener());
// execute the swingworker
longRunningProcess.execute();
}
}
// listen for state changes in our SwingWorker
private class DataProcessListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(LongRunningSwProcess.COUNT)) {
setCount((int)evt.getNewValue());
} else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
longRunningProcessDone();
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("My App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MyApp());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

// mock up of SwingWorker for long-running action
class LongRunningSwProcess extends SwingWorker<Void, Integer> {
public static final String COUNT = "count";
private static final int MIN_TIME_OUT = 5;
private static final int MAX_TIME_OUT = 10;
private int count = 0;
@Override
protected Void doInBackground() throws Exception {
// all this mock up does is increment a count field 
// every second until timeOut reached
int timeOut = MIN_TIME_OUT + (int) (Math.random() * (MAX_TIME_OUT - MIN_TIME_OUT));
for (int i = 0; i < timeOut; i++) {
setCount(i);
TimeUnit.SECONDS.sleep(1);
}
return null;
}
// make count a "bounded" property -- one that will notify listeners if changed
public void setCount(int count) {
int oldValue = this.count;
int newValue = count;
this.count = newValue;
firePropertyChange(COUNT, oldValue, newValue);
}
public int getCount() {
return count;
}
}

相关内容

最新更新