JOptionPane.showInternalConfirmDialog() 框架图标

问题描述

我有一个使用 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
}

这是我得到的:

Screenshot

如果我使用框架作为父级而不是使用 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 的静态方法的工作原理如下:

  1. 创建一个 JOptionPane 并用参数准备它。
  2. 创建一个对话框(或内部框架)并准备好参数。
  3. JOptionPane(从第 1 步创建)添加到对话框(或内部框架)(从第 2 步创建)和所需的侦听器。
  4. 将对话框显示为模态(输入块)。
  5. 从选定的值中搜索用户提供的值并返回。

第 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() 方法通常对任何试图制作模态内部框架的人都有用。