带内联“遮罩/取消遮罩”按钮的Jwing JPasswordField

时间:2020-03-23 17:03:21

标签: java swing user-interface passwords textfield

因此,我制作了一个类,该类在JPasswordField中表现出一点眼睛,并允许屏蔽/取消屏蔽密码。

它看起来像这样:

Image of passwordfield

问题是,尽管使用Windows L&F看起来不错,但是对于IntelliJ L&F来说,它看起来绝对可怕,因为我只是为JPanel使用默认的PasswordField边框来容纳眼睛以及实际的JPasswordField。现在我在想,是否有办法通过直接将眼睛涂在JPasswordField上并且不忽略内部密码的范围来获得这种行为。意味着里面的文字永远不要画在眼后,光标也不能滑到眼后。

我想避免为此编写一个完全自定义的文本字段,因为这听起来像是在重新发明轮子。

2 个答案:

答案 0 :(得分:0)

您可以像这样将SpringLayout用作JLayeredPane

import java.awt.Dimension;
import java.awt.Font;
import javax.swing.AbstractButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPasswordField;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;

public class Main {

    private static void createAndShowGUI() {
        final AbstractButton show = new JCheckBox("Show");

        //Just hide every decorating colors:
        show.setBorderPainted(false);
        show.setContentAreaFilled(false);
        show.setFocusPainted(false);

        final JPasswordField pass = new JPasswordField(20);
        pass.setFont(new Font(Font.MONOSPACED, Font.PLAIN, pass.getFont().getSize()));

        final char echo = pass.getEchoChar();
        show.addActionListener(e -> {
            pass.setEchoChar(show.isSelected()? 0: echo);
            pass.requestFocusInWindow();
        });

        //Put the fields in a JLayeredPane:
        final JLayeredPane pane = new JLayeredPane();            
        pane.setLayout(new SpringLayout());
        pane.add(pass, JLayeredPane.DEFAULT_LAYER);
        pane.add(show, JLayeredPane.PALETTE_LAYER); //Layer over the DEFAULT_LAYER one.

        //Setup the layout of the pane:
        final SpringLayout layout = (SpringLayout) pane.getLayout();
        layout.putConstraint(SpringLayout.WEST, pass, 0, SpringLayout.WEST, pane);
        layout.putConstraint(SpringLayout.EAST, pass, 0, SpringLayout.EAST, pane);
        layout.putConstraint(SpringLayout.NORTH, pass, 0, SpringLayout.NORTH, pane);
        layout.putConstraint(SpringLayout.SOUTH, pass, 0, SpringLayout.SOUTH, pane);
        layout.putConstraint(SpringLayout.EAST, show, 0, SpringLayout.EAST, pane);
        layout.putConstraint(SpringLayout.NORTH, show, 0, SpringLayout.NORTH, pane);
        layout.putConstraint(SpringLayout.SOUTH, show, 0, SpringLayout.SOUTH, pane);

        //Set the preferred size of the pane (it's not automatic with SpringLayout):
        final Dimension showPrefSz = show.getPreferredSize(),
                        passPrefSz = pass.getPreferredSize();
        pane.setPreferredSize(new Dimension(showPrefSz.width + passPrefSz.width, Math.max(showPrefSz.height, passPrefSz.height)));

        final JFrame frame = new JFrame("Unmaskable password field");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(Main::createAndShowGUI);
    }
}

任何Component都可以使用,因此您可以放置​​带有 eye 图标的JButton而不是我使用的JCheckBox

如果需要的话,我假设您要转到按钮上的setBorderPainted(false)setContentAreaFilled(false)setFocusPainted(false)

请注意,根据docs,您不得在setOpaque(false)上呼叫AbstractButton,而应使用setContentAreaFilled(false)

以下是相关的Java教程:

  1. How to Use SpringLayout
  2. How to Use Layered Panes

我在MRE中使用的SpringLayout.putConstraint命令首先告诉LayoutManagerJLayeredPane(整个4个第一个命令)填充整个JTextComponent,然后告诉将JCheckBox粘贴在JLayeredPane的东侧,并用JLayeredPane填充JCheckBox的整个高度,这将使{{ 1}}组件垂直居中(最后3个命令)。

注意:您需要设置JCheckBox的首选大小,因为JLayeredPane不会根据组件告诉它首选大小(例如其他SpringLayout会这样做) )。

答案 1 :(得分:0)

由于VGR建议我使用setMargin,这没有影响,所以我发现我可以通过使用使用原始JPasswordField边框的复合边框简单地实现相同的行为,外边框,以EmtpyBorder作为内边框,以便为显示/隐藏按钮腾出空间。这样,边界是正确的,当我与显示/隐藏图标进行交互时,我可以轻松编写可更改工具提示,光标和其他内容的代码。然后只需在paint的{​​{1}}方法中绘制图标即可。

更新

因此,显然,这种方法也不能很好地与L&F配合使用,我发现一种更安全的方法是重写JPasswordField,因为它没有设置方法。

另一个解决方案可能是getInsets()