简体   繁体   English

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

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

I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java.我了解使用事件调度线程对 Java 中的接口进行任何更改非常重要。 However, I have no idea how I can manipulate these events to stop/continue/start.但是,我不知道如何操纵这些事件来停止/继续/开始。 I want to refrain from moving on to the next line of main() (after the ones which put the Runnable in the EventQueue) until a certain key is pressed.在按下某个键之前,我想避免继续执行 main() 的下一行(在将 Runnable 放入 EventQueue 的行之后)。

I put together an example for clarity.为了清楚起见,我举了一个例子。 What I'd like to do here is spawn the JFrame, allow the user to move the box around with the arrow keys and then press Enter to cease the box-shifting operations, and ONLY then make the calculation at the end of main() and cause the answer to appear.我想在这里做的是生成 JFrame,允许用户使用箭头键移动框,然后按 Enter 停止框移动操作,然后仅在 main() 结束时进行计算并导致答案出现。 I should be able to get 400, 500, 600, etc. As it is, the calculation is made immediately after the JFrame appears, so the answer is always 300.我应该可以得到 400、500、600 等。因为它是在 JFrame 出现后立即进行计算的,所以答案总是 300。

I carved out a spot for whatever action should be bound to Enter;我为必须执行的任何操作留出了一个位置。 it's underneath the declarations for the actions bound to the arrow keys.它位于绑定到箭头键的操作的声明下方。

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()并且只有在 main() 结束时进行计算

That is not the way a GUI works.这不是 GUI 的工作方式。

The main() method is only used to display the frame. main() 方法仅用于显示框架。

Once the frame is visible the EDT is started and the frame sits there waiting for user events to be generated.一旦框架可见,EDT 就会启动,框架就坐在那里等待生成用户事件。

Your application code then responds to these user events.然后,您的应用程序代码会响应这些用户事件。

I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java.我了解使用事件调度线程对 Java 中的接口进行任何更改非常重要。

All code invoked in a listener does execute on the EDT.在侦听器中调用的所有代码都在 EDT 上执行。 So the code in your Action does execute on the EDT.因此,您的 Action 中的代码确实在 EDT 上执行。 You don't need to do anything special.你不需要做任何特别的事情。

What I want to trigger only on Enter我只想在 Enter 上触发

Then that logic should be contained in the Enter Action.然后该逻辑应包含在 Enter Action 中。

I would like to support what camickr said;我想支持camickr所说的; there is likely a better way to achieve what you are trying to do.可能有更好的方法来实现您正在尝试做的事情。 That said, if you really want to make your main method wait until the enter key is pressed, here's how:也就是说,如果你真的想让你的 main 方法等到按下回车键,方法如下:

First, at the top of your file, define an object to use as a synchronization lock like so:首先,在文件的顶部,定义一个 object 用作同步锁,如下所示:

public static final Object LOCK = new Object();

Then, in your main method, before your println statement, put the following code:然后,在您的 main 方法中,在您的 println 语句之前,输入以下代码:

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

What this does is it waits until the LOCK object's monitor lock is not being used by any thread (very simplified explanation, read more here ), and then it makes the current thread (in this case, the thread that started your main method) wait indefinitely.它的作用是等待LOCK对象的监视器锁没有被任何线程使用(非常简单的解释, 在这里阅读更多内容),然后它使当前线程(在这种情况下,启动你的 main 方法的线程)等待无限期地。

Next, add a throws declaration to the method header on your main method:接下来,在 main 方法的 header 方法中添加 throws 声明:

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

This tells the compiler that your code could throw an InterruptedException , which would happen if your thread was interrupted while it was waiting.这告诉编译器你的代码可能会抛出一个InterruptedException ,如果你的线程在等待时被中断,就会发生这种情况。

Finally, anywhere in your EndTheShifter constructor, put the following code:最后,在EndTheShifter构造函数的任何位置,输入以下代码:

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

This again waits until the LOCK object's monitor lock becomes available, and it then "notifies" all threads waiting on the LOCK object that they may continue.这再次等待直到LOCK对象的监视器锁变为可用,然后它“通知”所有在LOCK object 上等待的线程它们可以继续。 In this case, it will make our main thread continue and execute the println.在这种情况下,它会让我们的主线程继续执行 println。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM