簡體   English   中英

使用圖形界面(按鈕)在Java中停止循環

[英]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的圖像。

交通信號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.您可以使用ThreadSwingWorker

例:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM