因此,您希望显示图像并停止执行,直到窗口关闭。这只是向我尖叫模态对话框。一个模态对话框将从它可见的地方停止代码执行,它会以这样的方式执行,以免阻塞事件调度线程并使您的整个问题停止并挂起程序。详情请见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 传递出去。您需要结合使用 PropertyChangeListener 和 SwingWorkers 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,这取决于你,但你的核心问题似乎并不围绕它,而是显示一个窗口并暂停代码执行直到它关闭的能力