JOptionPane 获取密码

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案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.***.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://***.com/users/127479/adamski
 * @author agi-hammerthief https://***.com/users/2225787/agi-hammerthief
 * @see https://***.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://***.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;
    

【讨论】:

以上是关于JOptionPane 获取密码的主要内容,如果未能解决你的问题,请参考以下文章

从 JOptionPane 中删除图标

如何更改 JOptionPane 的背景颜色?

JOptionPane类提示框常用方法总结

JOptionPane 中的文本换行?

Java JOptionPane 默认文本

如何更改 JOptionPane.showInputDialog 中按钮的默认文本