0

我用java和2个按钮创建了一个图形界面。

我的目标 :

1)当我点击第一个按钮时,有一个循环处理不同的任务(按钮“开始”)。每个循环之间有 10 秒的停止

2)当我点击第二个按钮时,循环最后一次立即处理,但随后停止。(我也想弹出一个窗口显示它已停止,但这不是主要问题,我想我可以做到。)

我尝试了以下代码,但首先我认为它们是解决问题的更简单方法。另外我可以编译但它不起作用,循环没有停止,窗口崩溃:

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {

    globalStop="Run";

    while (globalStop.equals("Run")) {

        System.out.println("GO");
        // Other stuff

        // For the break ? 
        try {
            Thread.sleep(10000);
          } catch (InterruptedException ex) {
               Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
         }
    }
        System.out.println("done");
    }

}                                        

private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
    globalStop = "Stop";
    System.out.println("Bouton2");
}      

我希望我足够清楚,如果不是这样,请告诉我,我会改写。预先感谢大家的帮助。

4

4 回答 4

2

我想知道创建一个美国类型的交通信号 GUI 需要多长时间。花了75分钟。我能够快速创建 GUI,因为很多 Swing 都是样板文件。创建一个 GUI 后,您可以为下一个 GUI 复制一些类。

这是交通信号 GUI 的图像。

交通信号图形用户界面

当您按下开始按钮时,交通信号灯将从绿色循环到黄色再到红色。交通信号灯将永远循环,直到您按下停止按钮。

当您按下停止按钮时,交通信号灯将变为红色。它将永远保持红色,直到您按下开始按钮。

当您在交通信号灯循环时按下开始按钮时,从绿色到黄色到红色的循环重新开始。

基本上,以下步骤向您展示了如何创建任何 Swing GUI。我没有按此顺序创建代码,但按逻辑顺序解释代码是有意义的。所以,让我们深入研究代码。

这是 GUI 的模型类。每个 GUI 都需要有自己的模型,与应用程序的模型分开。对于这个 GUI,模型很简单。

package com.ggl.traffic.signal.model;

import java.awt.Dimension;

public class TrafficSignalModel {

    public static final int RED_LIGHT_TIME = 15;
    public static final int YELLOW_LIGHT_TIME = 5;
    public static final int GREEN_LIGHT_TIME = 10;

    public static final Dimension LIGHT_SIZE = new Dimension(32, 32);
}

我们在模型中设置信号灯时间,以及红绿灯的大小。

对于更复杂的 GUI,我们将跟踪模型中的字段值。

接下来,我们有交通信号 GUI 的主类。

package com.ggl.traffic.signal;

import javax.swing.SwingUtilities;

import com.ggl.traffic.signal.view.TrafficSignalFrame;

public class TrafficSignal implements Runnable {

    @Override
    public void run() {
        new TrafficSignalFrame();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new TrafficSignal());
    }

}

此类确保交通信号 GUI 在 Swing 事件线程上。这就是这堂课所做的一切。您可以看到如何复制此类以启动任何 GUI。

接下来,我们有 GUI 的 Frame 类。

package com.ggl.traffic.signal.view;

import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

public class TrafficSignalFrame {

    protected ButtonPanel bPanel;

    protected JFrame frame;

    protected TrafficSignalPanel tsPanel;

    public TrafficSignalFrame() {
        createPartControl();
    }

    protected void createPartControl() {
        tsPanel = new TrafficSignalPanel();
        bPanel = new ButtonPanel();

        bPanel.setTrafficSignalPanel(tsPanel);

        frame = new JFrame();
        frame.setTitle("Traffic Signal");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                exitProcedure();
            }
        });


        frame.setLayout(new FlowLayout());
        frame.add(bPanel.getPanel());
        frame.add(tsPanel.getPanel());
        frame.pack();
//      frame.setBounds(100, 100, 400, 200);
        frame.setVisible(true);
    }

    public void exitProcedure() {
        frame.dispose();
        System.exit(0);
    }

    public JFrame getFrame() {
        return frame;
    }

}

这个类是样板文件,除了构成 GUI 的特定 JPanel。如果您的 JFrame 有一个 JMenu,那么这就是将您的 JMenu 附加到您的 JFrame 的地方。

请注意,我没有扩展 JFrame 来创建此类。唯一一次扩展 Swing 组件是在重写一个或多个组件的方法时。如果我需要实际的 JFrame,我调用 getFrame() 方法。使用 Swing 组件而不是扩展 Swing 组件使我的方法与 Swing 方法分开。

接下来,我们来看看交通信号灯面板。该面板构成交通信号灯中的 3 个灯之一。

package com.ggl.traffic.signal.view;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JPanel;

public class TrafficSignalLightPanel extends JPanel {

    private static final long serialVersionUID = 1L;

    protected boolean lightOn;

    protected Color lightColor;
    protected Color darkColor;

    public TrafficSignalLightPanel(Color lightColor) {
        this.lightColor = lightColor;
        this.darkColor = Color.WHITE;
        this.lightOn = false;
    }

    public void setLightOn(boolean lightOn) {
        this.lightOn = lightOn;
        this.repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        if (lightOn) {
            g.setColor(lightColor);
        } else {
            g.setColor(darkColor);
        }
        g.fillRect(0, 0, getWidth(), getHeight());
    }

}

这个类扩展了 JPanel,因为我们想要重写paintComponent 方法。这是一个简单的类。它所做的只是将面板涂上颜色或白色。

接下来,我们将看看交通信号面板。此面板创建 3 个灯面板并将它们排列成垂直行。

package com.ggl.traffic.signal.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.Border;

import com.ggl.traffic.signal.model.TrafficSignalModel;

public class TrafficSignalPanel {

    protected JPanel panel;

    protected TrafficSignalLightPanel redLight;
    protected TrafficSignalLightPanel yellowLight;
    protected TrafficSignalLightPanel greenLight;

    public TrafficSignalPanel() {
        createPartControl();
    }

    protected void createPartControl() {
        Border border = BorderFactory.createLineBorder(Color.BLACK, 4);

        redLight = new TrafficSignalLightPanel(Color.RED);
        redLight.setBorder(border);
        redLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);

        yellowLight = new TrafficSignalLightPanel(Color.YELLOW);
        yellowLight.setBorder(border);
        yellowLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);

        greenLight = new TrafficSignalLightPanel(Color.GREEN);
        greenLight.setBorder(border);
        greenLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);

        panel = new JPanel();
        panel.setLayout(new FlowLayout());
        panel.setPreferredSize(
                new Dimension(TrafficSignalModel.LIGHT_SIZE.width + 10, 
                        TrafficSignalModel.LIGHT_SIZE.height * 3 + 25));

        panel.add(redLight);
        panel.add(yellowLight);
        panel.add(greenLight);
    }

    public JPanel getPanel() {
        return panel;
    }

    public TrafficSignalLightPanel getRedLight() {
        return redLight;
    }

    public TrafficSignalLightPanel getYellowLight() {
        return yellowLight;
    }

    public TrafficSignalLightPanel getGreenLight() {
        return greenLight;
    }

}

从 3 个 JPanel 创建一个 JPanel 相当简单。我设置了 JPanel 的首选大小,因此灯光将位于垂直行中。

接下来,我们来看看按钮面板。您几乎可以将此代码复制到任何具有按钮面板的 GUI 中。

package com.ggl.traffic.signal.view;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;

import com.ggl.traffic.signal.thread.TrafficSignalCycle;

public class ButtonPanel {

    protected JButton startButton;
    protected JButton stopButton;

    protected JPanel panel;

    protected TrafficSignalCycle thread;

    protected TrafficSignalPanel tsPanel;

    public ButtonPanel() {
        this.thread = null;
        createPartControl();
    }

    protected void createPartControl() {
        panel = new JPanel();
        panel.setLayout(new FlowLayout());

        startButton = new JButton("Start");
        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                if (thread != null) {
                    thread.stopRunning();
                }
                tsPanel.getRedLight().setLightOn(false);
                tsPanel.getYellowLight().setLightOn(false);
                tsPanel.getGreenLight().setLightOn(false);
                thread = new TrafficSignalCycle(tsPanel);
                thread.start();
            }
        });

        panel.add(startButton);

        stopButton = new JButton("Stop");
        stopButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                if (thread != null) {
                    thread.stopRunning();
                    thread = null;
                }
                tsPanel.getRedLight().setLightOn(true);
                tsPanel.getYellowLight().setLightOn(false);
                tsPanel.getGreenLight().setLightOn(false);
            }
        });

        panel.add(stopButton);

        setButtonSizes(startButton, stopButton);
    }

    protected void setButtonSizes(JButton ... buttons) {
        Dimension preferredSize = new Dimension();
        for (JButton button : buttons) {
            Dimension d = button.getPreferredSize();
            preferredSize = setLarger(preferredSize, d);
        }
        for (JButton button : buttons) {
            button.setPreferredSize(preferredSize);
        }
    }

    protected Dimension setLarger(Dimension a, Dimension b) {
        Dimension d = new Dimension();
        d.height = Math.max(a.height, b.height);
        d.width = Math.max(a.width, b.width);
        return d;
    }

    public void setTrafficSignalPanel(TrafficSignalPanel tsPanel) {
        this.tsPanel = tsPanel;
    }

    public JPanel getPanel() {
        return panel;
    }

}

按钮操作非常简单,我可以将它们保留在按钮面板中。如果需要,您可以编写单独的操作类。

最后,这是运行红绿灯循环的代码。它是 Thread 类的扩展,因此可以在与 GUI 不同的线程中运行。在与 GUI 线程分开的线程中工作总是一个好主意。

package com.ggl.traffic.signal.thread;

import javax.swing.SwingUtilities;

import com.ggl.traffic.signal.model.TrafficSignalModel;
import com.ggl.traffic.signal.view.TrafficSignalLightPanel;
import com.ggl.traffic.signal.view.TrafficSignalPanel;

public class TrafficSignalCycle extends Thread {

    protected boolean isRunning;
    protected boolean isFinished;

    protected TrafficSignalPanel tsPanel;

    public TrafficSignalCycle(TrafficSignalPanel tsPanel) {
        this.tsPanel = tsPanel;
        this.isRunning = true;
        this.isFinished = false;
    }

    @Override
    public void run() {
        while (isRunning) {
            signalLightOn(tsPanel.getGreenLight(), TrafficSignalModel.GREEN_LIGHT_TIME);
            signalLightOn(tsPanel.getYellowLight(), TrafficSignalModel.YELLOW_LIGHT_TIME);
            signalLightOn(tsPanel.getRedLight(), TrafficSignalModel.RED_LIGHT_TIME);
        }
        this.isFinished = true;
    }

    protected void signalLightOn(TrafficSignalLightPanel light, int seconds) {
        if (isRunning) {
            setLightOn(light, true);
        }

        for (int i = 0; i < 1000 && isRunning; i++) {
            try {
                Thread.sleep(1L * seconds);
            } catch (InterruptedException e) {
            }
        }
        setLightOn(light, false);
    }

    protected void setLightOn(final TrafficSignalLightPanel light,
            final boolean isLightOn) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                light.setLightOn(isLightOn);

            }       
        });
    }

    public void stopRunning() {
        this.isRunning = false;
        while (!isFinished) {
            try {
                Thread.sleep(10L);
            } catch (InterruptedException e) {
            }
        }
    }

}

真正改变信号灯颜色的方法必须在 Swing 事件线程中执行。这就是 setLightOn 方法通过调用 SwingUtilities 所做的。

定时循环有点复杂,因为我们希望能够在几毫秒内停止线程。isFinished 布尔值确保线程完全停止,以便可以设置灯光。

这是一个相当长的答案,但我希望它对任何创建 Swing GUI 的人都有帮助。

于 2012-08-21T17:37:42.457 回答
2

您不应该UI 线程中循环,也不应该让它进入睡眠状态。从根本上说,您应该尽可能保持 UI 线程自由。

如果您需要在 UI 线程中的 Swing UI 中定期发生某些事情,请使用 Swing Timer

但是,尚不清楚您在“其他内容”中正在做什么-您可能应该完全在不同的线程中执行此操作,并使用(例如) anAtomicBoolean来指示何时要停止。

于 2012-08-21T17:37:34.480 回答
1

1. You should always keep the UI thread for UI work and Non-UI thread for Non-UI work.

2. In Java GUI, the main() is not Long lived, after assigning the construction of GUI to the Event Dispatcher Thread, the main() quits, and now its EDT's responsibility handle the GUI.

3. So when you click the buttons, and the work you are doing is doing some heavy process or its time consuming....then span a Separate thread.

4. You can use Thread or SwingWorker.

Example:

Button b = new Button("Click me");

b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {

                              Thread t = new Thread(new Runnable(){


                                     public void run(){


                                           // Do the Heavy Processing work.....

                                       }

                                });

                t.start();
            }
        });
于 2012-08-21T17:42:14.450 回答
0

简单但肮脏的方式:

多线程你的程序,让一个线程做你的循环,第二个线程监控你的按钮。让按钮更改您的 globalStop 变量

不太容易但更清洁的方法:

使按钮引发中断以更改值。中断后for循环将继续到结束。

于 2012-08-21T17:38:09.987 回答