仅在按键时保留事件调度线程条目 (Java)

Leave Event Dispatch Thread entry ONLY on key press (Java)

我了解使用事件调度线程对 Java 中的界面进行任何更改很重要。但是,我不知道如何将这些事件操纵到 stop/continue/start。我想避免移动到 main() 的下一行(在将 Runnable 放入 EventQueue 的行之后),直到按下某个键。

为了清楚起见,我举了一个例子。我想在这里做的是生成 JFrame,允许用户使用箭头键移动框,然后按 Enter 停止框移动操作,然后才在 main() 结束时进行计算并使答案出现。我应该可以得到400、500、600等。因为JFrame出现后立即计算,所以答案总是300。

我为应该绑定到 Enter 的任何操作开辟了一个位置;它在绑定到箭头键的操作的声明下方。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class EndTheShifter extends JFrame
{

    private Color ourRectColor = new Color(28,222,144);
    private int ourRectWidth = 50;
    private int ourRectHeight = 50;

    protected static Point ourRecLocation = new Point(100,100);


    // Rectangle object can paint itself

    public class Rectangle
    {
        protected void paint(Graphics2D g2d)
        {
            g2d.setColor(ourRectColor);
            g2d.fillRect(ourRecLocation.x, ourRecLocation.y, ourRectWidth, ourRectHeight);
        }

    } // Rectangle class


    // OurRectangle can create a Rectangle and call paint() on it

    public class OurRectangle extends JPanel
    {

        private Rectangle capableRectangle;

        public OurRectangle()
        {
            capableRectangle = new Rectangle();
        }

        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D)g.create();

            capableRectangle.paint(g2d);

            g2d.dispose();
        }

    } // OurRectangle class


    KeyStroke pressRight = KeyStroke.getKeyStroke("RIGHT");
    KeyStroke pressLeft = KeyStroke.getKeyStroke("LEFT");
    KeyStroke pressUp = KeyStroke.getKeyStroke("UP");
    KeyStroke pressDown = KeyStroke.getKeyStroke("DOWN");
    KeyStroke pressEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);

    OurRectangle recToWorkWith = new OurRectangle();


    // Create InputMap and ActionMap

    InputMap inputMap = recToWorkWith.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
    ActionMap actionMap = recToWorkWith.getActionMap();

    // Mapping Shortcut

    protected void setTheAction(KeyStroke a, String b, Action c)
    {
        inputMap.put(a,b);
        actionMap.put(b,c);
    }


    // Constructor!!!

    public EndTheShifter()
    {

        add(recToWorkWith);

        Action rightAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                if(ourRecLocation.x != 600)
                    ourRecLocation.x += 50;
                else
                    ourRecLocation.x = 100;

                recToWorkWith.repaint();
            }
        };

        Action leftAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                if(ourRecLocation.x != 100)
                    ourRecLocation.x -= 50;
                else
                    ourRecLocation.x = 600;

                recToWorkWith.repaint();
            }
        };

        Action downAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                if(ourRecLocation.y != 600)
                    ourRecLocation.y += 50;
                else
                    ourRecLocation.y = 100;

                recToWorkWith.repaint();
            }
        };

        Action upAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                if(ourRecLocation.y != 100)
                    ourRecLocation.y -= 50;
                else
                    ourRecLocation.y = 600;

                recToWorkWith.repaint();
            }
        };

/*
        Action enterAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {

            }
        }

        setTheAction(pressEnter,"enterAction",enterAction);
*/

        setTheAction(pressRight,"rightAction",rightAction);
        setTheAction(pressLeft,"leftAction",leftAction);
        setTheAction(pressDown,"downAction",downAction);
        setTheAction(pressUp,"upAction",upAction);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(800,800);
        setVisible(true);
    }


    // Main kicks things off by putting all of the above
    // in the Event Dispatch thread

    // On an enter press, I want the last line of main() to run

    public static void main(String[] argv)
    {
        EventQueue.invokeLater(

        new Runnable()
        {
            @Override
            public void run()
            {
                new EndTheShifter();
            }
        });

        // What I want to trigger only on Enter

        System.out.println(ourRecLocation.x + 2*ourRecLocation.y);
    }

} // EndTheShifter, our outermost class

and ONLY then make the calculation at the end of main()

这不是 GUI 的工作方式。

main()方法仅用于显示框架。

一旦框架可见,EDT 就会启动,框架就在那里等待生成用户事件。

然后您的应用程序代码会响应这些用户事件。

I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java.

侦听器中调用的所有代码都在 EDT 上执行。因此,您的 Action 中的代码确实在 EDT 上执行。您不需要做任何特别的事情。

What I want to trigger only on Enter

那么该逻辑应该包含在输入操作中。

我支持camickr说的;可能有更好的方法来实现您想要做的事情。也就是说,如果你真的想让你的主方法等到按下回车键,方法如下:

首先,在您的文件顶部,定义一个用作同步锁的对象,如下所示:

public static final Object LOCK = new Object();

然后,在您的 main 方法中,在您的 println 语句之前,放置以下代码:

synchronized (LOCK) {
    LOCK.wait();
}

它的作用是等待 LOCK 对象的监视器锁未被任何线程使用(非常简单的解释,阅读更多 here),然后它使当前线程(在这种情况下,启动您的主要方法的线程)无限期等待。

接下来,在您的 main 方法的方法头中添加一个 throws 声明:

public static void main(String[] argv) throws InterruptedException

这告诉编译器您的代码可能会抛出一个 InterruptedException,如果您的线程在等待时被中断,就会发生这种情况。

最后,在您的 EndTheShifter 构造函数的任意位置,放置以下代码:

synchronized (LOCK) {
    LOCK.notify();
}

这再次等待,直到 LOCK 对象的监视器锁变得可用,然后它“通知”所有等待 LOCK 对象的线程它们可以继续。在这种情况下,它会让我们的主线程继续执行 println。