如何通过按键和释放来开始和结束循环?

How do I make a loop start and end with a key press and release?

这是我的代码...我怎样才能使它在用户按住按钮时运行循环并在用户释放按钮时停止?

public void nextPrimeNum()
{
    x = false;
    int b = 2;
    ArrayList<Integer> next = new ArrayList<Integer>();   
    while(x)
    {
        next = factors(b);
        if(next.size()==2)
        {
            System.out.println(b);
        }
        b++;
    }
    System.out.println("End");
}
public void keyPressed(KeyEvent e)
{
    if(e.getKeyCode() == 401)
    {
        x = true;
    }
}
public void keyRealesed(KeyEvent e)
{
    if(e.getKeyCode() == 402)
    {
        x = false;
    }
}

所以,答案是 - 这很复杂。它涵盖了广泛的主题,例如并发(一般)、GUI 开发、特定 API (Swing) 的最佳实践,通过阅读各种教程(和试验)

可以更详细地介绍这些主题

该示例提供了两种执行 "loop" 的方法(在 CalculateWorker class 的 doInBackground 方法中提供)。

您可以按住JButton或按住[kbd]Space[kbd]栏,两者都会导致"main loop"到运行,用结果更新 JTextArea...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTextArea ta;
        private CalculateWorker worker;

        public TestPane() {
            setLayout(new BorderLayout());

            ta = new JTextArea(20, 20);
            ta.setEditable(false);
            add(new JScrollPane(ta));

            worker = new CalculateWorker(ta);

            JButton btn = new JButton("Press");
            btn.getModel().addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    System.out.println("...isRunning = " + worker.isRunning());
                    if (!worker.isRunning()) {
                        return;
                    }
                    System.out.println("...isPressed = " + btn.getModel().isPressed());
                    System.out.println("...isPaused = " + worker.isPaused());
                    if (btn.getModel().isPressed()) {
                        worker.pause(false);
                    } else {
                        worker.pause(true);
                    }
                }
            });

            add(btn, BorderLayout.SOUTH);
            worker.execute();

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "Space.pressed");

            am.put("Space.released", new CalculateAction(false, worker));
            am.put("Space.pressed", new CalculateAction(true, worker));
        }

        public class CalculateWorker extends SwingWorker<List<String>, String> {

            private AtomicBoolean run = new AtomicBoolean(true);
            private AtomicBoolean paused = new AtomicBoolean(false);

            private ReentrantLock pausedLocked = new ReentrantLock();
            private Condition pausedCondition = pausedLocked.newCondition();

            private JTextArea ta;

            public CalculateWorker(JTextArea ta) {
                this.ta = ta;
                pause(true);
            }

            public void stop() {
                run.set(false);
                pausedLocked.lock();
                pausedCondition.signalAll();
                pausedLocked.unlock();
            }

            public void pause(boolean pause) {
                paused.set(pause);
                pausedLocked.lock();
                pausedCondition.signalAll();
                pausedLocked.unlock();
            }

            public boolean isPaused() {
                return paused.get();
            }

            public boolean isRunning() {
                return run.get();
            }

            @Override
            protected List<String> doInBackground() throws Exception {
                List<String> values = new ArrayList<>(256);
                long value = 0;
                System.out.println("!! Start running");
                while (run.get()) {
                    while (paused.get()) {
                        System.out.println("!! I'm paused");
                        pausedLocked.lock();
                        try {
                            pausedCondition.await();
                        } finally {
                            pausedLocked.unlock();
                        }
                    }
                    System.out.println("!! Start loop");
                    while (!paused.get() && run.get()) {
                        value++;
                        values.add(Long.toString(value));
                        publish(Long.toString(value));
                        Thread.sleep(5);
                    }
                    System.out.println("!! Main loop over");
                }
                System.out.println("!! Run is over");
                return values;
            }

            @Override
            protected void process(List<String> chunks) {
                for (String value : chunks) {
                    ta.append(value);
                    ta.append("\n");
                }
                ta.setCaretPosition(ta.getText().length());
            }

        }

        public class CalculateAction extends AbstractAction {

            private boolean start;
            private CalculateWorker worker;

            public CalculateAction(boolean start, CalculateWorker worker) {
                putValue(NAME, "Calculate");
                this.start = start;
                this.worker = worker;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                worker.pause(start);
            }

        }

    }

}

Is there a simpler solution?

当然,我总是先寻找最困难、最难理解的解决方案(讽刺)

虽然 "might" 可以降低复杂性,但该示例提供了一些 "best practice" 概念,您可以很好地学习和理解这些概念。

根据所使用的API,解决方案也可以不同,因此,这是针对特定API选择的"simplest"解决方案。

I wanted to do it from the console!

Java 做不到 - 它的控制台支持充其量只是基本的,不支持 "key pressed/released" 操作的概念(因为它是 运行 在单个线程中运行,否则它就不可能这样做了)。

您可能会尝试 "are" 解决方案,但它们需要链接到本机二进制文件的第三方库才能实施,这将(可能)减少它 运行 在[上的平台数量=23=]

GUI 和多线程编程本身就很困难。
所以,这很简单,没有过多违反最佳实践。

你需要几样东西:

  • 用于打印素数的单独 Thread
    它的 run 方法会一直循环,但会在 Space 键未按下时暂停。
    (有关详细信息,请参阅 Defining and Starting a Thread
  • A KeyListener 将从 AWT 的事件调度线程调用:
    事件处理方法旨在快速完成,以便其他事件 (例如移动、调整大小和关闭框架)仍然可以快速处理。
    (参见 How to Write a Key ListenerThe Event Dispatch Thread 了解更多信息)
  • 用于添加 KeyListener
  • 的可见 GUI 组件 (JFrame)
  • 2 个线程之间的一些同步(通过 synchronizednotifywait) 这样 keyPressed 上的 prime-printing starts/continues 并在 keyReleased
    暂停 (有关详细信息,请参阅 Guarded Blocks
  • 通过调用 initGUI.
    初始化并启动整个 GUI (有关详细信息,请参阅 Initial Threads

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Main implements Runnable, KeyListener {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(Main::initGUI);
    }

    private static void initGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JLabel("Press SPACE key for printing primes"));
        frame.pack();
        frame.setLocationRelativeTo(null); // center on screen
        frame.setVisible(true);

        Main main = new Main();
        frame.addKeyListener(main);
        Thread thread = new Thread(main);
        thread.start();
    }

    private boolean spaceKeyPressed;

    private boolean isPrime(int n) {
        for (int i = 2; i < n; i++) {
            if (n % i == 0)
                return false;
        }
        return true;
    }

    @Override
    public void run() {
        for (int n = 2; /**/; n++) {
            while (!spaceKeyPressed) {
                synchronized (this) {
                    try {
                        wait(); // waits until notify()
                    } catch (InterruptedException e) {
                        // do nothing
                    }
                }
            }
            if (isPrime(n)) {
                System.out.println(n);
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // do nothing
    }

    @Override
    public synchronized void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_SPACE) {
            spaceKeyPressed = true;
            notifyAll(); // cause wait() to finish
        }
    }

    @Override
    public synchronized void keyReleased(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_SPACE) {
            spaceKeyPressed = false;
            notifyAll(); // cause wait() to finish
        }
    }
}