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

时间:2018-08-11 00:14:44

标签: java event-handling awt keylistener event-listener

这是我的代码...如何使它工作,以便在用户按住按钮时运行循环,而在用户释放按钮时停止循环?

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;
    }
}

2 个答案:

答案 0 :(得分:1)

GUI和多线程编程本质上是困难的。
因此,这非常简单,而且不会过多违反最佳做法。

您需要满足以下条件:

  • 单独的Thread用于打印素数:
    它的run方法永远循环,但是在不按 Space 键时暂停。
    (有关更多信息,请参见Defining and Starting a Thread
  • 一个KeyListener,它将从AWT的事件分派线程中调用:
    事件处理方法旨在快速完成,以便其他事件 (例如移动,调整框架大小和关闭框架)仍然可以快速处理。
    (请参见How to Write a Key ListenerThe Event Dispatch Thread了解更多信息)
  • 用于添加JFrame的可见GUI组件(KeyListener
  • 两个线程之间有一些同步(通过synchronizednotifywait) 以便在keyPressed上开始/继续原始打印 并在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
        }
    }
}

答案 1 :(得分:0)

所以,答案是-很复杂。它涵盖了广泛的主题,例如并发性(一般),GUI开发,使用特定API的最佳实践(Swing),通过阅读各种教程(和实验性文章)可以更好地详细介绍

该示例提供了两种执行“循环”的方法(在doInBackground类的CalculateWorker方法中提供了该方法)。

您可以按住JButton或按住[kbd] Space [kbd]条,这两者都会导致“主循环”运行,并用结果更新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);
            }

        }

    }

}
  

有没有更简单的解决方案?

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

尽管可以“降低”复杂度,但是该示例提出了一些“最佳实践”概念,您可以很好地学习和理解。

根据所使用的API,解决方案也可以不同地完成,因此,这是针对特定API选择的“最简单”解决方案。

  

我想从控制台做到这一点!

Java无法做到这一点-它的控制台支持最多是基本的,并且不支持“按键/释放”操作的概念(因为它在单个线程中运行,否则就不可能这样做) )。

您可以尝试“有”解决方案,但是它们需要链接到本机二进制文件的第三方库来实现,这(可能)将减少其运行平台的数量