【问题标题】:Java wait code until JFrame keyPressedJava 等待代码直到 JFrame keyPressed
【发布时间】:2017-05-25 09:42:03
【问题描述】:

我正在使用 JFrame,我想显示图像并暂停代码,直到用户按下 ANY 键。按下该键后,图像将关闭,代码将继续运行。

我做了什么:

  • 创建了一个标志

    final boolean[] flag = {true};

  • 向 JFrame 对象添加了一个 addKeyListener 来更改标志

    frame.addKeyListener(new KeyListener() {
    
    @Override
    public void keyTyped(KeyEvent e) {
    }
    
    @Override
    public void keyReleased(KeyEvent e) {
    }
    
    @Override
    public void keyPressed(KeyEvent e) {
        frame.setVisible(false);
        frame.dispose();
        flag[0] = false;
    }
    });
    
  • 等待循环直到被标记

    while (flag[0]){
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

这行得通,但我知道它有点足智多谋。

还有其他方法可以制作等待循环吗?有没有听者的听者?

第二次尝试,使用 CountDownLatch

  • 设置闩锁

        final CountDownLatch latch = new CountDownLatch(1);
    
  • 倒计时 for (JFrame 框架 : framesList) {

        frame.addKeyListener(new KeyListener() {
    
            @Override
            public void keyTyped(KeyEvent e) {
            }
    
            @Override
            public void keyReleased(KeyEvent e) {
            }
    
            @Override
            public void keyPressed(KeyEvent e) {
                    frame.setVisible(false);
                    frame.dispose();
                    latch.countDown();
            }
        });
    
  • 等一下

        latch.await();
    

【问题讨论】:

  • 我不明白你的问题的这一部分:only then I would like to continue the code.。你能解释一下你的意思吗?您的应用程序是否因为 JFrame 消失而终止?
  • @OvidiuDolha 我加了一个例子
  • 谢谢,但我还是不明白...你能把代码贴在你显示/更改图像的地方吗?你的 while 循环现在到底放在哪里?
  • @OvidiuDolha 完整的code is here。我从另一个函数调用它Mat img = Imgcodecs.imread( args[0], Imgcodecs.IMREAD_COLOR ); HighGui.imshow("Colour", img); HighGui.waitKey(0); // 以灰度显示图像img = Imgcodecs.imread( args[0], Imgcodecs.IMREAD_GRAYSCALE ); HighGui.imshow("Gray", img); HighGui.waitKey(0);
  • @JoãoCartucho Latch 方法对我来说看起来不错,但实际上简单的模态对话框会做同样的事情,例如 JOptionPane.showMessageDialog 阻塞直到对话框被释放。如果您在对话框中显示图像,您将实现这两​​个目标。在与图像的对话关闭后,调用代码将继续执行。

标签: java jframe


【解决方案1】:

因此,您希望显示图像并停止执行,直到窗口关闭。这只是向我尖叫模态对话框。一个模态对话框将从它可见的地方停止代码执行,它会以这样的方式执行,以免阻塞事件调度线程并使您的整个问题停止并挂起程序。详情请见How to use dialogs...

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Test {

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

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    BufferedImage img = ImageIO.read(...);
                    ImageShower.show(null, img);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static class ImageShower extends JPanel {

        private JLabel label = new JLabel();

        public ImageShower() {
            setLayout(new BorderLayout());
            add(label);

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

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
            am.put("close", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Window window = SwingUtilities.windowForComponent(ImageShower.this);
                    if (window != null) {
                        window.dispose();
                    }
                }
            });
        }

        public void setImage(Image img) {
            label.setIcon(new ImageIcon(img));
        }

        public static void show(Component owner, Image img) {
            Window parent = null;
            if (owner != null) {
                parent = SwingUtilities.windowForComponent(owner);
            }

            JButton close = new JButton("Close");
            close.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JButton btn = (JButton) e.getSource();
                    Window window = SwingUtilities.windowForComponent(btn);
                    if (window != null) {
                        window.dispose();
                    }
                }
            });

            JDialog dialog = new JDialog(parent, Dialog.ModalityType.APPLICATION_MODAL);
            ImageShower shower = new ImageShower();
            shower.setImage(img);
            dialog.add(shower);
            dialog.add(close, BorderLayout.SOUTH);
            dialog.getRootPane().setDefaultButton(close);
            dialog.pack();
            dialog.setLocationRelativeTo(owner);
            dialog.setVisible(true);
        }

    }

}

“但是等等,可能图像很大并且加载需要时间,我不想在加载时冻结 UI”...

好的,为此,我希望使用SwingWorker,它可以在后台加载图像,但它提供了简单的方法来确保图像在 EDT 的上下文中正确显示...

public class ImageLoadAndShow extends SwingWorker<Void, Image> {

    @Override
    protected Void doInBackground() throws Exception {
        BufferedImage img = ImageIO.read(...);
        publish(img);
        return null;
    }

    @Override
    protected void process(List<Image> chunks) {
        Image img = chunks.get(chunks.size() - 1);
        ImageShower.show(null, img);
    }

}

不是,如果图片加载失败,你不会知道,因为doInBackground 方法会将Exception 传递出去。您需要结合使用 PropertyChangeListenerSwingWorkers get 方法来捕获它,请记住,get 是阻塞的,因此在 EDT 的上下文中调用它会阻塞,直到 worker完成

“但我需要在对话框关闭时进行其他操作”

有几种方法可以实现这一点,这取决于你想要做什么,对于这个例子,我坚持使用SwingWorker,因为它很容易复制和粘贴基本的结构,但您可以使用 Runnable 包裹在 Thread

public class ImageLoadShowAndWait extends SwingWorker<Void, Void> {

    @Override
    protected Void doInBackground() throws Exception {
        BufferedImage img = ImageIO.read(...);
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                ImageShower.show(null, img);
            }
        });
        return null;
    }

}

现在,如果这些都不能满足您的要求...那么我想知道您实际上在做什么:P,请查看Foxtrot,它提供了一个 API,允许您执行在 EDT 中异步代码而不阻塞它(完全),但这会在它被调用的地方停止代码执行,直到它完成

问题是我希望它在按下任意键时关闭 JFrame

KeyListener 会给你带来问题,也许不是今天,也许不是明天,但它最终会在你面前爆发。我提供的示例绑定了 Escape 键来处理窗口。它还使“关闭”按钮成为默认按钮,该按钮还提供 Space 和/或 Enter 键,并为用户提供了一个漂亮的视觉队列。

如果你想使用KeyListener,这取决于你,但你的核心问题似乎并不围绕它,而是显示一个窗口并暂停代码执行直到它关闭的能力

【讨论】:

  • 效果很好!我这样做的原因是我正在尝试为 Java API 做 OpenCV HighGui 模块,如果我能做到,这将帮助很多人。
  • Code Here,如果我从特定方法而不是主方法调用类可以吗?我希望人们只是做HighGui2.imshow("title", imgMat);
  • 是的,从你需要的任何地方调用它,只要确保你在 EDT 的上下文中调用它
  • 那我可以擦掉main方法吗?如果我想一次显示多个图像/对话框,您是否建议将它们全部连接到同一个 JPanel 中或保留单独的窗口?
  • @JoãoCartucho 好吧,您不需要我的main 方法,但最终您将不再需要它。如果您想一次显示多个图像,我会很想使用单个窗口
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多