3

在 Java 中,假设我有一个带有 2 个按钮的 GUI,Go 和 Pause。

当我按 Go 时,“Hello”会一遍又一遍地打印出来。当我按下暂停时,“Hello”不再打印到屏幕上。

示例:用户按下 Go 按钮。“你好”会打印 1 分钟,直到用户按下“暂停”。

在 Java 中表达这种方法的正确方法是什么?它是否等同于我在 goButton 源代码中注释的伪代码?

public void actionPerformed(ActionEvent e) {
    if(e.getSource() == goButton)
    {
        // while user has not pressed the pause button
        printHello();
    }
    else if(e.getSource() == pauseButton)
    {
        pause();
    }
}

谢谢

4

7 回答 7

3

要拥有响应式 UI,您通常必须printHello()在单独的线程中运行。然后,当您在此线程中进行处理时,例如,在每个打印语句之后,您检查一些标志boolean isPaused;并在它为真时停止执行。pause单击按钮时,将此标志的值设置为true

于 2012-09-24T03:42:05.600 回答
3

为了让它以合理的方式工作,您将需要一个Thread. 这将在后台执行,直到您决定取消/暂停它。

这是一个非常基本的例子。通常我会将任务和 GUI 包装在适当的类中,而不是访问静态引用,但它提供了一个基本概念

public class TestHello {

    private static HelloTask task;

    public static void main(String[] args) {

        Thread thread = new Thread((task = new HelloTask()));
        thread.setDaemon(true);
        thread.start();

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new GridBagLayout());
        frame.setSize(200, 200);
        frame.setLocationRelativeTo(null);

        JButton goButton = new JButton("Go");
        JButton stopButton = new JButton("Stop");

        goButton.setActionCommand("Go");
        stopButton.setActionCommand("Stop");

        ActionHandler handler = new ActionHandler();

        goButton.addActionListener(handler);
        stopButton.addActionListener(handler);

        frame.add(goButton);
        frame.add(stopButton);

        frame.setVisible(true);

    }

    public static class ActionHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            if (e.getActionCommand().equals("Go")) {
                task.start();
            } else if (e.getActionCommand().equals("Stop")) {
                task.pause();
            }

        }

    }

    public static class HelloTask implements Runnable {

        private static final Object WAIT_LOCK = new Object();
        private boolean dump = false;

        public void start() {
            synchronized (WAIT_LOCK) {
                dump = true;
                WAIT_LOCK.notify();
            }
        }

        public void pause() {
            synchronized (WAIT_LOCK) {
                dump = false;
                WAIT_LOCK.notify();
            }
        }

        @Override
        public void run() {
            while (true) {
                while (dump) {
                    System.out.println("Hello");
                }
                try {
                    synchronized (WAIT_LOCK) {
                        WAIT_LOCK.wait();
                    }
                } catch (Exception e) {
                }
            }
        }
    }
}

一些进一步阅读:

注意事项

切勿尝试从除事件调度线程之外的任何线程修改 GUI。

于 2012-09-24T03:49:29.660 回答
0

您需要在单独的线程中实现循环。否则,GUI 将变得无响应,用户可能根本无法单击暂停按钮。

使用这种线程化方法,您还需要一个标志来指示是否打印出消息。当标志设置为不再打印时,打印循环可以简单地停止执行线程。

于 2012-09-24T03:41:05.523 回答
0

您可以通过几种最简单的方式做到这一点:

您有一个布尔标志,keepPrinting当您按下 Go 按钮时将其设置为 true,当您按下 Pause 时将其设置为 false。接下来,您有一个线程在某处执行 while 循环,当 keepPrinting 为 false 时将不打印任何内容。

这里的线程非常重要,没有它,一旦用户按下按钮,你的 GUI 就会冻结,因为程序会打印 hello 并愉快地忽略其他任何内容。

伪代码

//GUI
public ThreadedPrinter greeter;

void ButtonGoPushed(args){
    greeter.keepPrinting = true;
}

void ButtonPausePushed(args){
    greeter.keepPrinting = false;
}

//ThreadedPrinter

boolean keepPrinting
void run(){
    while(true){
        if(keepPrinting){
           print("Hello");
        }
        sleep(5); //Make sure that this thread yields if the system doesn't do it automatically
}

关于 java 并发与说 C++ 的好消息是,这将起作用,您不必担心布尔值是疯狂的和不一致的,因为在 java 中变量集是原子的。如果您想做的不仅仅是设置变量,请创建一个synchronized方法来设置变量并执行您想做的任何其他事情。

于 2012-09-24T03:42:12.543 回答
0

那么htis呢:

 boolean flag=true;
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == goButton)
        {
            while(true)
       {
            printHello();
       }
        }
        else if(e.getSource() == pauseButton)
        {

            pause();
        }
    }
于 2012-09-24T03:42:40.977 回答
0

基本上为了保持 UI 响应,这样的任务需要在其他线程中执行。

可以有多种方式在 java 中实现这种机制。我使用了简单的机制Runnalbevolatile标志来确保调用cancelPrint()方法时线程存在

public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
  //start the thread here 
}
else if(e.getSource() == pauseButton)
{
    //call cancel here
}
}

public class HelloPrinter implements Runnable {

volatile boolean cancel = false;

@Override
public void run() {

    while (!cancel) {
        printHello();
    }
}

public void cancelPrint() {
    cancel = true;
}
}
于 2012-09-24T03:57:51.927 回答
0

我假设您想做的不仅仅是打印输出。看看Swing Worker

它允许您非常轻松地编写在 AWT 事件线程中执行的与 GUI 相关的代码以及在其他线程中执行的长时间执行代码,并来回传递值。这将有助于防止您可能遇到的任何 GUI 锁定问题。

于 2012-09-24T04:44:11.367 回答