问题描述
我有一个使用 JDesktopPane 的应用程序,我正在使用 JOptionPane.showInternalConfirmDialog() 来获得用户的确认。我遇到的问题是,无论我使用什么作为父级,我总是将咖啡杯作为框架图标。查看示例代码(不带 try-catch):
JFrame frame = new JFrame();
frame.setDefaultCloSEOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(300,300);
frame.setIconImage(ImageIO.read(new File("icon.png")));
JDesktopPane dp = new JDesktopPane();
frame.add(dp);
frame.setVisible(true);
if(JOptionPane.showInternalConfirmDialog(frame.getContentPane(),"Are you sure?","Confirm",JOptionPane.YES_NO_OPTION) == 0)
{
//Do something
}
这是我得到的:
如果我使用框架作为父级而不是使用 getContentPane() 我会得到这个异常:
Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: JOptionPane: parentComponent does not have a valid parent
如果您对此问题有任何见解,我们将不胜感激。
编辑:我知道创建新内部框架的解决方法如下:
JOptionPane jop = new JOptionPane("Are you sure?",JOptionPane.YES_NO_OPTION);
JInternalFrame inf = jop.createInternalFrame(frame,"Confirm");
inf.setFrameIcon(icon);
inf.setVisible(true);
但是窗口不再是模态的,而我需要它。
解决方法
好吧,为了更改对话框的图标图像,您需要获取对由 JOptionPane
的静态方法创建的对话框的引用。
JOptionPane
的静态方法的工作原理如下:
- 创建一个
JOptionPane
并用参数准备它。 - 创建一个对话框(或内部框架)并准备好参数。
- 将
JOptionPane
(从第 1 步创建)添加到对话框(或内部框架)(从第 2 步创建)和所需的侦听器。 - 将对话框显示为模态(输入块)。
- 从选定的值中搜索用户提供的值并返回。
第 2 步和第 3 步发生在 create*
的 JOptionPane
方法中。
所以您可以尝试以下操作:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Objects;
import javax.swing.JDesktopPane;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class OptionPaneCustomIconDialog {
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
final JDesktopPane pane = new JDesktopPane();
pane.setPreferredSize(new Dimension(500,500));
final JFrame frame = new JFrame("No coffee...");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
final BufferedImage dialogImage = new BufferedImage(100,100,BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = dialogImage.createGraphics();
g2d.setColor(Color.RED);
g2d.fillRect(25,25,50,50);
g2d.dispose();
final JOptionPane option = new JOptionPane("Are you sure?",JOptionPane.QUESTION_MESSAGE,JOptionPane.YES_NO_OPTION);
final JDialog diag = option.createDialog(pane,"Please confirm...");
diag.setIconImage(dialogImage); //The solution.
diag.setVisible(true); //Blocks (because it's modal).
final Object value = option.getValue();
if (value == null || Objects.equals(value,JOptionPane.CLOSED_OPTION))
System.out.println("Closed");
else if (Objects.equals(value,JOptionPane.YES_OPTION))
System.out.println("Yes");
else if (Objects.equals(value,JOptionPane.NO_OPTION))
System.out.println("No");
else
System.err.println("Please implement all options...");
});
}
}
,
根据 docs,您可以将 Icon
作为参数传递给 showInternalConfirmDialog
方法。请记住,然后以正确的顺序添加所有其余参数。
好的,这是我想出的解决方案。我看了一下 JOptionPane,它使用一个名为 startModal() 的私有方法来使内部框架模态。
我将该方法和静态 showInternalConfirmDialog() 复制到一个新类中,添加了一个 ImageIcon 作为参数并使用 setFrameIcon() 更改图标。
注意:我还必须在内部框架上使用 setVisible() 和 requestFocus() 才能使其工作。
const vowels = new Set(['a','e','i','o','u','A','E','I','O','U']); // set is more efficient data structure for lookups
function getVowels(sentence) {
return sentence
.split('') // this returns an array of all the letters in a string,i.e. ['H','l',' ','W','r','d']
.filter(letter => vowels.has(letter)); // this checks every element of an array to match a condition and returns an array of all elements where the check returned `false`
}
console.log(getVowels('Hello World'));
console.log(getVowels('AaEeIiOoUu'));
console.log(getVowels('aaaaa'));
用法:
import java.awt.AWTEvent;
import java.awt.ActiveEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.MenuComponent;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import javax.swing.ImageIcon;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class InternalDialog {
public static int showInternalConfirmDialog(Component parentComponent,Object message,String title,int optionType,int messageType,ImageIcon icon)
{
JOptionPane pane = new JOptionPane(message,messageType,optionType);
JInternalFrame frame = pane.createInternalFrame(parentComponent,title);
//WHAT I ADDED
frame.setFrameIcon(icon);
frame.setVisible(true);
frame.requestFocus();
startModal(frame);
if (pane.getValue() instanceof Integer)
return ((Integer) pane.getValue()).intValue();
return -1;
}
private static void startModal(JInternalFrame f){
// We need to add an additional glasspane-like component directly
// below the frame,which intercepts all mouse events that are not
// directed at the frame itself.
JPanel modalInterceptor = new JPanel();
modalInterceptor.setOpaque(false);
JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
lp.setLayer(modalInterceptor,JLayeredPane.MODAL_LAYER.intValue());
modalInterceptor.setBounds(0,lp.getWidth(),lp.getHeight());
modalInterceptor.addMouseListener(new MouseAdapter(){});
modalInterceptor.addMouseMotionListener(new MouseMotionAdapter(){});
lp.add(modalInterceptor);
f.toFront();
// We need to explicitly dispatch events when we are blocking the event
// dispatch thread.
EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
try
{
while (! f.isClosed())
{
if (EventQueue.isDispatchThread())
{
// The getNextEventMethod() issues wait() when no
// event is available,so we don't need do explicitly wait().
AWTEvent ev = queue.getNextEvent();
// This mimics EventQueue.dispatchEvent(). We can't use
// EventQueue.dispatchEvent() directly,because it is
// protected,unfortunately.
if (ev instanceof ActiveEvent)
((ActiveEvent) ev).dispatch();
else if (ev.getSource() instanceof Component)
((Component) ev.getSource()).dispatchEvent(ev);
else if (ev.getSource() instanceof MenuComponent)
((MenuComponent) ev.getSource()).dispatchEvent(ev);
// Other events are ignored as per spec in
// EventQueue.dispatchEvent
}
else
{
// Give other threads a chance to become active.
Thread.yield();
}
}
}
catch (InterruptedException ex)
{
// If we get interrupted,then leave the modal state.
}
finally
{
// Clean up the modal interceptor.
lp.remove(modalInterceptor);
// Remove the internal frame from its parent,so it is no longer
// lurking around and clogging memory.
Container parent = f.getParent();
if (parent != null)
parent.remove(f);
}
}
}
startModal() 方法通常对任何试图制作模态内部框架的人都有用。