【问题标题】:JOptionPane to get passwordJOptionPane 获取密码
【发布时间】:2012-02-11 11:09:54
【问题描述】:

JOptionPane 可用于从用户获取字符串输入,但就我而言,我想在showInputDialog 中显示密码字段。

我需要的方式是屏蔽用户输入的内容,返回值必须在char[]。我需要一个带有消息、密码字段和两个按钮的对话框。可以这样做吗?谢谢。

【问题讨论】:

    标签: java swing user-interface passwords joptionpane


    【解决方案1】:

    您可以创建自己的扩展 JDialog 的对话框,然后您可以在其中放入任何您想要的内容。

    【讨论】:

      【解决方案2】:

      是的,可以使用JOptionPane.showOptionDialog()。像这样的:

      JPanel panel = new JPanel();
      JLabel label = new JLabel("Enter a password:");
      JPasswordField pass = new JPasswordField(10);
      panel.add(label);
      panel.add(pass);
      String[] options = new String[]{"OK", "Cancel"};
      int option = JOptionPane.showOptionDialog(null, panel, "The title",
                               JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE,
                               null, options, options[1]);
      if(option == 0) // pressing OK button
      {
          char[] password = pass.getPassword();
          System.out.println("Your password is: " + new String(password));
      }
      

      【讨论】:

      • JPasswordField(10) 不接受超过 10 的密码。最好将其设置得更宽或使用下面的 @Adamski 之类的无参数构造函数。 OK 也应该是默认选项,因为许多用户在输入密码后会出于习惯按 Enter。如果取消是默认的,那么输入密码然后回车将取消对话框。
      • 这个很好,在 JOptionPane 中带有 JPasswordField 的标签,在我的情况下,它接受的密码超过 10 个。
      • 当对话框出现时如何让密码字段成为焦点?
      • 这不起作用。当我在密码字段中按 Enter 时,选项 == 1。
      • 通过选项[0] 使 OK 成为默认值。当您在密码字段中按 Enter 时,这将返回 option==0。
      【解决方案3】:

      最简单的方法是使用JOptionPaneshowConfirmDialog 方法并传入对JPasswordField 的引用;例如

      JPasswordField pf = new JPasswordField();
      int okCxl = JOptionPane.showConfirmDialog(null, pf, "Enter Password", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
      
      if (okCxl == JOptionPane.OK_OPTION) {
        String password = new String(pf.getPassword());
        System.err.println("You entered: " + password);
      }
      

      编辑

      下面是使用自定义JPanelJPasswordField 一起显示消息的示例。根据最近的评论,我还(匆忙地)添加了代码以允许 JPasswordField 在对话框首次显示时获得焦点。

      public class PasswordPanel extends JPanel {
        private final JPasswordField passwordField = new JPasswordField(12);
        private boolean gainedFocusBefore;
      
        /**
         * "Hook" method that causes the JPasswordField to request focus the first time this method is called.
         */
        void gainedFocus() {
          if (!gainedFocusBefore) {
            gainedFocusBefore = true;
            passwordField.requestFocusInWindow();
          }
        }
      
        public PasswordPanel() {
          super(new FlowLayout());
      
          add(new JLabel("Password: "));
          add(passwordField);
        }
      
        public char[] getPassword() {
            return passwordField.getPassword();
        }
      
        public static void main(String[] args) {
            PasswordPanel pPnl = new PasswordPanel();
            JOptionPane op = new JOptionPane(pPnl, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
      
            JDialog dlg = op.createDialog("Who Goes There?");
      
            // Wire up FocusListener to ensure JPasswordField is able to request focus when the dialog is first shown.
            dlg.addWindowFocusListener(new WindowAdapter() {
              @Override
              public void windowGainedFocus(WindowEvent e) {
                  pPnl.gainedFocus();
              }
            });
      
            if (op.getValue() != null && op.getValue().equals(JOptionPane.OK_OPTION)) {
                String password = new String(pPnl.getPassword());
                System.err.println("You entered: " + password);
            }
        }
      }
      

      【讨论】:

      • 正是我想要的。顺便说一句,有没有办法设置该对话框的消息/正文字符串
      • 创建 JPanel 的最简单方法,其中包含 JPasswordField 以及包含所需文本的 JLabel。
      • 我已经知道了,但我不想在应用程序运行期间打开另一个框架。所以我想将您的解决方案与 message string 一起使用。
      • 我不确定你的意思。打开另一个框架:自定义 JPanel 可以作为消息传递给对 JOptionPane.showConfirmDialog 的调用。查看我最近的编辑。
      • @AdamMackler:我已经修改了代码来解决这个问题。
      【解决方案4】:

      如果你这样做,这个对话看起来会好很多

      dlg.setVisible(true);
      

      否则你根本看不到它。

      还有

      pPnl.gainedFocus();
      

      应该是

      pPnl.gainedFocus();
      

      除此之外,它工作得很好。感谢您的代码。使用 Swing 节省了我面对周围的时间。

      另外,如果您不想让对话在每次打开时都在后台运行,您需要使用类似的方式关闭它

      dlg.dispatchEvent(new WindowEvent(dlg, WindowEvent.WINDOW_CLOSING));
      dlg.dispose(); // else java VM will wait for dialog to be disposed of (forever)
      

      【讨论】:

        【解决方案5】:

        以下类是Adamski's great answer的扩展:

        package com.stackoverflow.swing;
        
        import java.awt.Component;
        import java.awt.Dimension;
        import java.awt.FlowLayout;
        import java.awt.event.WindowAdapter;
        import java.awt.event.WindowEvent;
        import javax.swing.JDialog;
        import javax.swing.JLabel;
        import javax.swing.JOptionPane;
        import javax.swing.JPanel;
        import javax.swing.JPasswordField;
        
        /**
         * Class that creates a panel with a password field. Extension of Adamski's class
         *
         * @author adamski https://stackoverflow.com/users/127479/adamski
         * @author agi-hammerthief https://stackoverflow.com/users/2225787/agi-hammerthief
         * @see https://stackoverflow.com/a/8881370/2225787
         */
        public class PasswordPanel extends JPanel {
        
          private final JPasswordField JFieldPass;
          private JLabel JLblPass;
          private boolean gainedFocusBefore;
        
          /**
           * "Hook" method that causes the JPasswordField to request focus when method is
           * first  called.
           */
          public void gainedFocus () {
            if (!gainedFocusBefore) {
              gainedFocusBefore = true;
              JFieldPass.requestFocusInWindow();
            }
          }
        
          public PasswordPanel (int length) {
            super(new FlowLayout());
            gainedFocusBefore = false;
            JFieldPass = new JPasswordField(length);
            Dimension d = new Dimension();
            d.setSize(30, 22);
            JFieldPass.setMinimumSize(d);
            JFieldPass.setColumns(10);
            JLblPass = new JLabel("Password: ");
            add(JLblPass);
            add(JFieldPass);
          }
        
          public PasswordPanel() {
            super(new FlowLayout());
            gainedFocusBefore = false;
            JFieldPass = new JPasswordField();
            Dimension d = new Dimension();
            d.setSize(30, 22);
            JFieldPass.setMinimumSize(d);
            JFieldPass.setColumns(10);
            JLblPass = new JLabel("Password: ");
            add(JLblPass);
            add(JFieldPass);
          }
        
          public char[] getPassword() {
            return JFieldPass.getPassword();
          }
        
          public String getPasswordString() {
            StringBuilder passBuilder = new StringBuilder();
        
            char[] pwd = this.getPassword();
            if (pwd.length > 0) {
              for (char c : pwd) {
                passBuilder.append(c);
              }
            }
        
            return passBuilder.toString();
          }
        
          private static String displayDialog (
            Component parent, final PasswordPanel panel, String title
          ) {
            String password = null;
            /* For some reason, using `JOptionPane(panel, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE)`
            does not give the same results as setting values after creation, which is weird */
            JOptionPane op = new JOptionPane(panel);
            op.setMessageType(JOptionPane.QUESTION_MESSAGE);
            op.setOptionType(JOptionPane.OK_CANCEL_OPTION);
            JDialog dlg = op.createDialog(parent, title);
            // Ensure the JPasswordField is able to request focus when the dialog is first shown.
            dlg.addWindowFocusListener (new WindowAdapter () {
              @Override
              public void windowGainedFocus (WindowEvent e) {
                panel.gainedFocus ();
              }
            });
            dlg.setDefaultCloseOperation (JOptionPane.OK_OPTION); // necessary?
        
            dlg.setVisible (true);
        
            Object val = op.getValue ();
            if (null != val && val.equals (JOptionPane.OK_OPTION)) {
              password = panel.getPasswordString();
            }
        
            return password;
          }
        
          public static String showDialog (Component parent, String title) {
            final PasswordPanel pPnl = new PasswordPanel();
            return displayDialog(parent, pPnl, title);
          }
        
          public static String showDialog (
            Component parent, String title, int passwordLength
          ) {
            final PasswordPanel pPnl = new PasswordPanel(passwordLength);
        
            return displayDialog (parent, pPnl, title);
          }
        
          public static String showDialog (String title) {
            return showDialog(null, title);
          }
        
          public static String showDialog (String title, int passwordLength) {
            return showDialog(null, title, passwordLength);
          }
        
          /**
           * Show a test dialog
           */
          public static void main(String[] args) {
            String test = PasswordPanel.showDialog ("Enter Your Password");
            System.out.println ("Entered Password: " + test);
            System.exit(0);
          }
        
        }
        

        【讨论】:

          【解决方案6】:

          基于Adamski's answer 的 Kotlin 解决方案。

          这次没有焦点按钮,只需输入密码并按 Enter,或 Escape 放弃输入:

          class PasswordPanel : JPanel(FlowLayout()) {
          
              private val passwordField = JPasswordField(20)
              private var entered = false
          
              val enteredPassword
                  get() = if (entered) String(passwordField.password) else ""
          
              init {
                  add(JLabel("Password: "))
                  add(passwordField)
                  passwordField.setActionCommand("OK")
                  passwordField.addActionListener {
                      if (it.actionCommand == "OK") {
                          entered = true
          
                          // https://stackoverflow.com/a/51356151/1020871
                          SwingUtilities.getWindowAncestor(it.source as JComponent)
                              .dispose()
                      }
                  }
              }
          
              private fun request(passwordIdentifier: String) = apply {
                  JOptionPane.showOptionDialog(null, this@PasswordPanel,
                      "Enter $passwordIdentifier",
                      JOptionPane.DEFAULT_OPTION,
                      JOptionPane.INFORMATION_MESSAGE,
                      null, emptyArray(), null)
              }
          
              companion object {
          
                  fun requestPassword(passwordIdentifier: String) = PasswordPanel()
                      .request(passwordIdentifier)
                      .enteredPassword
              }
          }
          

          用法:PasswordPanel.requestPassword(passwordIdentifier)

          【讨论】:

            【解决方案7】:

            尝试在@Adamski@Peter West的原始解决方案之后简化代码。

            所有的接线都在PasswordPanel类中完成,客户端只调用静态getPassword方法。

            String passwordText = PasswordPanel.getPassword();
            if (null != passwordText) {
                System.out.println("password: <" + passwordText + ">");
            }
            

            来源:

            import javax.swing.*;
            import java.awt.*;
            import java.awt.event.WindowAdapter;
            import java.awt.event.WindowEvent;
            
            public class PasswordPanel extends JPanel {
                private final String password;
            
                private PasswordPanel(String prompt) {
                    super(new FlowLayout());
                    JPasswordField pwdField = new JPasswordField(12);
                    add(pwdField);
                    JOptionPane joptionPane = new JOptionPane(this, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
                    boolean responseOK = configure(joptionPane, prompt, pwdField).equals(JOptionPane.OK_OPTION);
                    this.password = responseOK ? String.valueOf(pwdField.getPassword()) : null;
                }
            
                public static String getPassword(){
                    return new PasswordPanel("Input Password ... ").password;
                }
            
                private Object configure(JOptionPane jOptionPane, String prompt, JComponent pwdField) {
                    JDialog jDialog = promptDialog(prompt, jOptionPane, pwdField);
                    Object result = jOptionPane.getValue();
                    jDialog.dispatchEvent(new WindowEvent(jDialog, WindowEvent.WINDOW_CLOSING));
                    jDialog.dispose();
                    return result;
                }
            
                private JDialog promptDialog(String message, JOptionPane jOptionPane, JComponent pwdField) {
                    JDialog dialog = jOptionPane.createDialog(message);
                    dialog.addWindowFocusListener(new WindowAdapter() {
                        @Override
                        public void windowGainedFocus(WindowEvent e) {
                            pwdField.requestFocusInWindow();
                        }
                    });
                    dialog.setVisible(true);
                    return dialog;
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-08-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多