[英]Stop a loop in java with graphical interface (buttons)
我用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");
}
我希望我足夠清楚,如果不是這種情況,請告訴我,我將重新表述。 預先感謝大家的幫助。
您不應該在 UI線程中循環,也不應該讓它進入睡眠狀態。 從根本上講,您應該使UI線程盡可能自由。
如果您需要在UI線程中的Swing UI中定期發生某些事情,請使用Swing Timer 。
尚不清楚您在“其他內容”中正在做什么-可能您應該完全在另一個線程中執行此操作,並使用(例如) AtomicBoolean
指示何時停止。
我想知道創建美國類型的交通信號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的特定JPanels之外。 如果您的JFrame具有JMenu,則可以在此處將JMenu附加到JFrame。
請注意,我沒有擴展JFrame來制作此類。 唯一的擴展Swing組件的方法是覆蓋一個或多個組件的方法。 如果需要實際的JFrame,則調用getFrame()方法。 使用Swing組件而不是擴展Swing組件可使我的方法與Swing方法分開。
接下來,我們來看看交通信號燈面板。 該面板組成交通信號燈中的三個指示燈之一。
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個JPanels相當簡單地創建一個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的任何人都有幫助。
1.您應始終保留用於UI工作的UI線程和用於Non-UI工作的Non-UI線程 。
2.在Java GUI中,在將GUI的結構分配給Event Dispatcher Thread
之后, main()
並不是長期存在的, main()
退出了,現在它的EDT負責處理該GUI。
3.因此,當您單擊按鈕時,您所做的工作正在做一些繁重的過程或非常耗時...。然后跨越一個Separate thread
。
4.您可以使用Thread
或SwingWorker
。
例:
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();
}
});
簡單但骯臟的方式:
多線程執行程序,並讓一個線程執行循環,第二個線程監視您的按鈕。 讓按鈕更改您的globalStop變量
不太容易但更干凈的方法:
使按鈕引發中斷以更改值。 中斷后,for循環將繼續到結束。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.