Summary

JPasswordFieldに入力されたパスワードを可視化したとき、数値のみ色を変更して字形が似たアルファベットとの判別を簡単にします。

Source Code Examples

JPasswordField password = new JPasswordField(40);
password.setFont(FONT);
password.setText("!1l2c$%34e5&6#7=8g9O0");
JTextPane revealPassword = makeRevealPassword(password);
CardLayout cardLayout = new CardLayout();
JPanel p = new JPanel(cardLayout) {
  @Override public void updateUI() {
    super.updateUI();
    setAlignmentX(RIGHT_ALIGNMENT);
  }
};
p.add(password, PasswordField.HIDE.toString());
p.add(revealPassword, PasswordField.SHOW.toString());

AbstractButton button = new JToggleButton();
button.addActionListener(e -> {
  boolean b = ((AbstractButton) e.getSource()).isSelected();
  if (b) {
    copyText(password.getDocument(), revealPassword.getStyledDocument());
    cardLayout.show(p, PasswordField.SHOW.toString());
  } else {
    copyText(revealPassword.getStyledDocument(), password.getDocument());
    cardLayout.show(p, PasswordField.HIDE.toString());
  }
});
initEyeButton(button);

JPanel panel = new OverlayLayoutPanel();
panel.add(button);
panel.add(p);

// ...
class HighlightDocumentFilter extends DocumentFilter {
  private final SimpleAttributeSet defAttr = new SimpleAttributeSet();
  private final SimpleAttributeSet numAttr = new SimpleAttributeSet();
  private final Pattern pattern = Pattern.compile("\\d");

  protected HighlightDocumentFilter() {
    super();
    StyleConstants.setForeground(defAttr, Color.BLACK);
    StyleConstants.setForeground(numAttr, Color.RED);
  }

  @Override public void insertString(
      FilterBypass fb, int offset, String text, AttributeSet attr)
      throws BadLocationException {
    super.insertString(fb, offset, text, attr);
    update(fb);
  }

  @Override public void remove(
      FilterBypass fb, int offset, int length)
      throws BadLocationException {
    super.remove(fb, offset, length);
    update(fb);
  }

  @Override public void replace(
      FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
      throws BadLocationException {
    super.replace(fb, offset, length, text, attrs);
    update(fb);
  }

  private void update(FilterBypass fb) throws BadLocationException {
    StyledDocument doc = (StyledDocument) fb.getDocument();
    String text = doc.getText(0, doc.getLength());
    doc.setCharacterAttributes(0, doc.getLength(), defAttr, true);
    Matcher m = pattern.matcher(text);
    while (m.find()) {
      doc.setCharacterAttributes(m.start(), m.end() - m.start(), numAttr, true);
    }
  }
}
View in GitHub: Java, Kotlin

Description

  • JPasswordField#setEchoChar(...) + HighlightFilter
    • JPasswordField#setEchoChar(...)をオーバーライドし、エコー文字として0(ヌル文字、\u0000)が設定されパスワードが可視化された場合は、数値のみHighlighterを使用して強調表示するDocumentFilterを追加
    • 0以外のエコー文字(このサンプルではUIManager.get("PasswordField.echoChar")で取得した文字)が設定された場合は、Highlighter#removeAllHighlights()で全ハイライトの削除と上記のDocumentFilterの削除を実行
class DigitHighlightPasswordField extends JPasswordField {
  protected DigitHighlightPasswordField(int columns) {
    super(columns);
  }

  @Override public void setEchoChar(char c) {
    super.setEchoChar(c);
    Document doc = getDocument();
    if (doc instanceof AbstractDocument) {
      boolean reveal = c == '\u0000';
      if (reveal) {
        ((AbstractDocument) doc).setDocumentFilter(new HighlightFilter(this));
        try {
          doc.remove(0, 0);
        } catch (BadLocationException ex) {
          UIManager.getLookAndFeel().provideErrorFeedback(this);
        }
      } else {
        getHighlighter().removeAllHighlights();
        ((AbstractDocument) doc).setDocumentFilter(null);
      }
    }
  }
}

Reference

Comment