Качайте JPasswordField со встроенной кнопкой Маска / Маска - PullRequest
0 голосов
/ 23 марта 2020

Итак, я создал класс, который немного смотрит в JPasswordField и позволяет маскировать / снимать маску с пароля.

Это выглядит так:

Image of passwordfield

Проблема в том, что, несмотря на то, что при использовании Windows L & F все выглядит нормально, с IntelliJ L & F это выглядит просто ужасно, поскольку я просто использую границу PasswordField по умолчанию для JPanel, в котором находится глаз, и фактический JPasswordField. Теперь мне было интересно, есть ли способ получить такое поведение, рисуя глаз прямо в JPasswordField, а также не игнорируя границы пароля внутри. Это означает, что текст внутри никогда не должен быть нарисован за глазом, и курсор не должен быть в состоянии go за глазом.

Я хотел бы избежать написания полностью настраиваемого текстового поля для этого, так как это звучит как переизобретение колесо.

Ответы [ 2 ]

0 голосов
/ 24 марта 2020

Поскольку VGR рекомендовал мне использовать setMargin, который не имел никакого эффекта, я обнаружил, что могу просто добиться того же поведения, используя составную границу, которая использует исходную границу JPasswordField в качестве внешней границы, и EmtpyBorder в качестве внутренней границы, чтобы освободить место для кнопки «показать / скрыть». Таким образом, границы правильны, и я могу легко написать код, который изменяет подсказку, курсор и еще что-то при взаимодействии со значком «показать / скрыть». Затем значок просто рисуется в методе paint из JPasswordField.

ОБНОВЛЕНИЕ

Так что, очевидно, этот подход также не очень хорошо работал с L & F и я обнаружил, что более безопасным подходом было бы просто переопределить getInsets(), поскольку для него не существует метода установки.

Еще одним решением могло бы быть JLayeredPane.

0 голосов
/ 24 марта 2020

Вы можете использовать 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 будет работать, так что вы можете поставить JButton с глазом значок вместо JCheckBox, который я использую.

Я предполагаю, что вы собираетесь setBorderPainted(false), setContentAreaFilled(false) и setFocusPainted(false) на кнопке, если это вообще необходимо.

Помните, что в соответствии с документами вы не должны звонить setOpaque(false) на AbstractButton и вместо этого использовать setContentAreaFilled(false).

Вот соответствующие Java учебные пособия:

  1. Как использовать SpringLayout .
  2. Как использовать многоуровневые панели .

The SpringLayout.putConstraint Команды, которые я использую в MRE, сначала говорят LayoutManager, чтобы заполнить весь JLayeredPane JTextComponent (4 первые команды), а затем вставляют JCheckBox на восточной стороне JLayeredPane и также заполните всю высоту JLayeredPane JCheckBox, что делает текст и флажок компонента JCheckBox вертикально центрированными (3 последние команды).

Примечание: вам нужно установить предпочтительный размер JLayeredPane, потому что SpringLayout не скажет ему предпочтительный размер в соответствии с компонентами (такими как другие LayoutManager s).

...