在程序运行时将计时器保持在JPanel中

时间:2017-06-20 21:18:34

标签: java multithreading timer jpanel

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

App.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();
            }
        });
    }

}

MainFrame(处理JPanel和数据进程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);
    }

StatusPanel(显示计时器等的面板)

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

}

Timer.java(StatusPanel中使用的计时器)

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

}

使用actionlisteners在dataControlsPanel中完成数据处理。

1 个答案:

答案 0 :(得分:2)

  

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

首先,您的计时器应该是javax.swing.Timer或“Swing”计时器。这是为了专门针对Swing事件线程而构建的,所以应该避免当前代码显示的许多Swing线程问题 - 例如,这里:timeLabel.setText(timeFormat.format(currentTime)); - 这使得来自后台线程的Swing调用和是危险的代码。下一个 处理代码应该进入SwingWorker。当工作程序执行时,您可以通过调用Timer上的stop()来暂停Swing Timer,或者只是让计时器继续运行。当SwingWorker完成其操作时 - 我经常通过添加到SwingWorker的PropertyChangeListener监听,监听其state属性更改为SwingWorker.StateValue.DONE,在工作者上调用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;
    }
}