Java Swing如何取消无限循环。

4

我在Swing中遇到了无限循环的问题。经过一些研究,我发现了SwingWorker线程,但不确定如何实现它们。我编写了一个简单的程序来展示这个问题。一个按钮启动了无限循环,我希望另一个按钮停止它,但由于Swing单线程问题,另一个按钮已经冻结了。以下是代码,需要帮助:

public class Model
{
    private int counter;
    private boolean go = true;

    public void go()
    {
        counter = 0;

        while(go)
        {
            counter++;
            System.out.println(counter);
        }
    }

    public int getCounter()
    {
        return counter;
    }

    public void setGo(boolean value)
    {
        this.go = value;
    }
}

public class View extends JFrame
{
    private JPanel                  topPanel, bottomPanel;
    private JTextArea               messageArea;
    private JButton                 startButton, cancelButton;
    private JLabel                  messageLabel;
    private JScrollPane             scrollPane;

    public View()
    {
        setSize(250, 220);
        setTitle("View");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        topPanel = new JPanel();
        bottomPanel = new JPanel();
        messageArea = new JTextArea(8, 20);
        messageArea.setEditable(false);
        scrollPane = new JScrollPane(messageArea);
        messageLabel = new JLabel("Message Area");
        topPanel.setLayout(new BorderLayout());
        topPanel.add(messageLabel, "North");
        topPanel.add(scrollPane, "South");
        startButton = new JButton("START");
        cancelButton = new JButton("CANCEL");
        bottomPanel.setLayout(new GridLayout(1, 2));
        bottomPanel.add(startButton);
        bottomPanel.add(cancelButton);
        Container cp = getContentPane();
        cp.add(topPanel, BorderLayout.NORTH);
        cp.add(bottomPanel, BorderLayout.SOUTH);
    }

    public JButton getStartButton()
    {
        return startButton;
    }

    public JButton getCancelButton()
    {
        return cancelButton;
    }

    public void setMessageArea(String message)
    {
        messageArea.append(message + "\n");
    }
}


public class Controller implements ActionListener
{
    private Model theModel;
    private View  theView;

    public Controller(Model model, View view)
    {
        this.theModel = model;
        this.theView = view;
        view.getStartButton().addActionListener(this);
        view.getCancelButton().addActionListener(this);
    }

    public void actionPerformed(ActionEvent ae)
    {
        Object buttonClicked = ae.getSource();
        if(buttonClicked.equals(theView.getStartButton()))
        {
            theModel.go();
        }
        else if(buttonClicked.equals(theView.getCancelButton()))
        {
            theModel.setGo(false);
        }
    }
}



public class Main
{
    public static void main(String[] args)
    {
        Model model = new Model();
        View view = new View();
        Controller controller = new Controller(model, view);
        view.setVisible(true);
    }
}
4个回答

3

您可以轻松地实现此功能,而无需实现任何计时器,只需要将以下两行添加到您的actionPerformed方法中:

public void actionPerformed(ActionEvent ae)
{
    Object buttonClicked = ae.getSource();
    if(buttonClicked.equals(theView.getStartButton()))
    {
      theModel.setGo(true); //make it continue if it's just stopped
      Thread t = new Thread(new Runnable() { public void run() {theModel.go();}}); //This separate thread will start the new go...
      t.start(); //...when you start the thread! go!
    }
    else if(buttonClicked.equals(theView.getCancelButton()))
    {
        theModel.setGo(false);
    }
}

由于您的 Model.go() 运行在一个独立线程中,事件分配线程 可以自由进行其工作,例如绘画释放按钮,而不是挂起在按下按钮上。
然而,有一个限制! 由于运行 Model.go() 的线程将会 疯狂运转! 它实际上被调用的次数几乎与系统每秒钟能够处理的次数相同。
如果您计划实现一些动画或类似的内容,则需要:
  • 使用定时器,

或者

例如,如果您选择使用线程:

public void go()
{
    counter = 0;
        while(go)
    {
        counter++;
        System.out.println(counter);
        try {
            Thread.sleep(1500); //Sleep for 1.5 seconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

如您所见,我添加了Thread.sleep(1500),其中1500是以毫秒为单位的时间(1.5秒)。 Thread.sleep可能会因某些原因而中断,因此必须捕获InterruptedException
在这种特定情况下,没有必要深入处理正确的InterruptedException,但如果您感到好奇,可以阅读这篇不错的文章

3
你正在阻塞事件分派线程(Event Dispatch Thread,EDT)。该线程负责处理绘制和其他与UI相关的请求。一旦EDT被阻塞,UI将会冻结,因为它无法处理任何事件。有关详细信息,请参见事件分派线程教程。
考虑使用定时器(如何使用Swing定时器)、SwingWorker或辅助后台线程。后台线程可以使用SwingUtilities.invokeLater()与EDT通信。这种机制已经在SwingWorker中实现,所以可能更容易使用。具体取决于所需的功能。

3

使用 javax.swing.Timer 在事件处理中使用 start()stop() 一次执行 go()(可延迟),实现工作。


0

我决定使用SwingWorker线程,以下是更新的Controller类。它能够完成我需要的工作,但我的问题是,这是否是正确的方式,以及代码是否干净整洁?此外,我尝试将model.go()方法的输出传递到视图的文本区域中,按照注释行但没有成功,有人知道如何做吗?

public class Controller implements ActionListener
{
private Model theModel;
private View  theView;
private SwingWorker<Void, Void> worker;

public Controller(Model model, View view)
{
    this.theModel = model;
    this.theView = view;
    view.getStartButton().addActionListener(this);
    view.getCancelButton().addActionListener(this);
}

public void actionPerformed(ActionEvent ae)
{
    Object buttonClicked = ae.getSource();
    if(buttonClicked.equals(theView.getStartButton()))
    {
        theModel.setGo(true);
        worker = new SwingWorker<Void, Void>()
        {
            @Override
            protected Void doInBackground()
            {
                // theView.setMessageArea(theModel.getCounterToString());
                return theModel.go();
            }
            @Override
            protected void done()
            {
                // theView.setMessageArea(theModel.getCounterToString());
            }
        };
        worker.execute();

    }
    else if(buttonClicked.equals(theView.getCancelButton()))
    {
        theModel.setGo(false);
    }
}
}


public class Model
{
    public Void go()
    {
        counter = 0;

        while(go)
        {
            counter++;
            System.out.println(counter);
        }
        return null;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接