java - 如何在其他 JTextField 下找到 JTextField?

标签 java swing user-interface layout layout-manager

我正在制作身份验证 GUI,它应该包含 2 个文本字段,用户名 JTextField 和密码 JPasswordField。我想让密码字段位于用户名字段下方。我目前的代码如下:

public class GuiAuthentication extends JFrame {

private static final int WIDTH  = 1000;
private static final int HEIGHT =  650;

private JTextField     tfUsername;
private JPasswordField tfPassword;

public GuiAuthentication() {
    try {
        setTitle("Metaspace Launcher");

        getContentPane().setLayout(new FlowLayout());
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        setSize(WIDTH, HEIGHT);

        tfUsername = new JTextField("Username");
        tfPassword = new JPasswordField("********");

        tfUsername.setBounds(10, 10, 50, 20);
        tfPassword.setBounds(10, 50, 50, 20);

        getContentPane().add(tfUsername);
        getContentPane().add(tfPassword);

        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setMinimumSize  (new Dimension(WIDTH, HEIGHT));
        setMaximumSize  (new Dimension(WIDTH, HEIGHT));

        setResizable(false);
        requestFocus();

        setLocationRelativeTo(null);
        setVisible(true);
    } catch (final Exception ex) {
        ex.printStackTrace();
        System.exit(ex.toString().hashCode());
    }
}

@Override
public void paint(final Graphics g) {
    super.paint(g);

    Gui.drawBackground(this, g, WIDTH, HEIGHT);
    Gui.drawRect(g, WIDTH/3, HEIGHT/3 + 20, 325, 200, 0xAA000000);
}

但是,这会导致密码字段位于用户名字段的对面,并且它们都居中,而不是位于我指定的 X 位置 10:

--> Screenshot (click)

问题出在我目前使用的布局 (FlowLayout) 上吗?如果是这样,那我应该使用哪一个?如果不是,还有什么问题?

也尝试使用 GridBagLayout。结果为 this (字段并排,居中):

最佳答案

您的 GridBagLayout 尝试图像表明您在添加组件时没有使用 GridBagConstraints 或者您使用的不正确。

如果你想在 GUI 中将你的文本组件居中,一个在另一个之上,然后说把它们放在它们自己的盒子里,然后使用 GridBagLayout 作为容纳它们的容器,并传入适当的 GridBagConstraints,这将很好地工作随着你的愿望。这将意味着为约束提供适当的 gridx 和 gridy 值以匹配您希望在网格中放置组件的位置。此外,您还需要正确锚定组件,并且通常需要设置约束插图以在组件周围提供一个空白空间缓冲区,这样它们就不会相互拥挤。我自己,当我做这样的事情时,我经常使用两行两列的 4 x 4 网格,包括左侧的一列 JLabel,以便用户知道右列中的每个文本组件代表什么。我经常使用辅助方法来帮助创建约束,如下所示:

private GridBagConstraints createGbc(int x, int y) {
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridx = x;
    gbc.gridy = y;
    gbc.fill = GridBagConstraints.HORIZONTAL; // stretch components horizontally
    gbc.weightx = 1.0;
    gbc.weighty = 0.0; // increase if you want component location to stretch vert.

    // I_GAP is a constant and is the size of the gap around
    // each component
    gbc.insets = new Insets(I_GAP, I_GAP, I_GAP, I_GAP);

    // if the x value is odd, anchor to the left, otherwise if even to the right
    gbc.anchor = x % 2 == 0 ? GridBagConstraints.WEST : GridBagConstraints.EAST;
    return gbc;
}

然后我会像这样使用它:

JPanel innerPanel = new JPanel(new GridBagLayout());

JLabel userNameLabel = new JLabel("User Name:");
userNameLabel.setForeground(Color.LIGHT_GRAY);
innerPanel.add(userNameLabel, createGbc(0, 0)); // add w/ GBC
innerPanel.add(tfUsername, createGbc(1, 0)); // etc...
JLabel passwordLabel = new JLabel("Password:");
passwordLabel.setForeground(Color.LIGHT_GRAY);
innerPanel.add(passwordLabel, createGbc(0, 1));
innerPanel.add(tfPassword, createGbc(1, 1));

一个工作示例可能如下所示:

enter image description here

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;    
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.Border;

@SuppressWarnings("serial")
public class MetaSpaceLauncherPanel extends JPanel {
    // path to a public starry image
    public static final String IMG_PATH = "https://upload.wikimedia.org/wikipedia/"
            + "commons/thumb/b/be/Milky_Way_at_Concordia_Camp%2C_Karakoram_Range%2"
            + "C_Pakistan.jpg/1280px-Milky_Way_at_Concordia_Camp%2C_Karakoram_Range"
            + "%2C_Pakistan.jpg";
    private static final int I_GAP = 10;

    private static final int COLS = 15;
    private JTextField tfUsername = new JTextField(COLS);
    private JPasswordField tfPassword = new JPasswordField(COLS);
    private BufferedImage background = null;

    public MetaSpaceLauncherPanel(BufferedImage background) {
        this.background = background;

        // close window if enter pressed and data within fields
        ActionListener listener = e -> {
            String userName = tfUsername.getText().trim();
            char[] password = tfPassword.getPassword();

            Window window = SwingUtilities.getWindowAncestor(MetaSpaceLauncherPanel.this);
            if (userName.isEmpty() || password.length == 0) {
                // both fields need to be filled!
                String message = "Both user name and password fields must contain data";
                String title = "Invalid Data Entry";
                JOptionPane.showMessageDialog(window, message, title, JOptionPane.ERROR_MESSAGE);
            } else {
                // simply close the dialog
                window.dispose();
            }
        };

        tfUsername.addActionListener(listener);
        tfPassword.addActionListener(listener);

        JPanel innerPanel = new JPanel(new GridBagLayout());
        innerPanel.setOpaque(false);
        Border outerBorder = BorderFactory.createEtchedBorder();
        Border innerBorder = BorderFactory.createEmptyBorder(I_GAP, I_GAP, I_GAP, I_GAP);
        Border border = BorderFactory.createCompoundBorder(outerBorder, innerBorder);
        innerPanel.setBorder(border);

        JLabel userNameLabel = new JLabel("User Name:");
        userNameLabel.setForeground(Color.LIGHT_GRAY);
        innerPanel.add(userNameLabel, createGbc(0, 0));
        innerPanel.add(tfUsername, createGbc(1, 0));
        JLabel passwordLabel = new JLabel("Password:");
        passwordLabel.setForeground(Color.LIGHT_GRAY);
        innerPanel.add(passwordLabel, createGbc(0, 1));
        innerPanel.add(tfPassword, createGbc(1, 1));

        setLayout(new GridBagLayout());
        add(innerPanel); // add without constraints to center it
    }

    public String getUserName() {
        return tfUsername.getText();
    }

    public char[] getPassword() {
        return tfPassword.getPassword();
    }

    private GridBagConstraints createGbc(int x, int y) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = x;
        gbc.gridy = y;
        gbc.fill = GridBagConstraints.HORIZONTAL; // stretch components horizontally
        gbc.weightx = 1.0;
        gbc.weighty = 0.0; // increase if you want component location to stretch vert.

        // I_GAP is a constant and is the size of the gap around
        // each component
        gbc.insets = new Insets(I_GAP, I_GAP, I_GAP, I_GAP);

        // if the x value is odd, anchor to the left, otherwise if even to the right
        gbc.anchor = x % 2 == 0 ? GridBagConstraints.WEST : GridBagConstraints.EAST;
        return gbc;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (background != null) {
            g.drawImage(background, 0, 0, this);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet() || background == null) {
            return super.getPreferredSize();
        }
        int w = background.getWidth();
        int h = background.getHeight();
        return new Dimension(w, h);
    }

    private static void createAndShowGui() {
        BufferedImage img = null;
        try {
            // just using this as an example image, one available to all
            // you would probably use your own image
            URL imgUrl = new URL(IMG_PATH); // online path to starry image
            img = ImageIO.read(imgUrl);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1); // no image available -- exit!
        }
        MetaSpaceLauncherPanel launcherPanel = new MetaSpaceLauncherPanel(img);

        JDialog dialog = new JDialog(null, "MetaSpace Launcher", ModalityType.APPLICATION_MODAL);
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.getContentPane().add(launcherPanel);
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);

        // test to see if we can get the data
        String userName = launcherPanel.getUserName();
        char[] password = launcherPanel.getPassword();

        // don't convert password into String as I'm doing below as it is now
        // not secure
        String message = String.format("<html>User Name: %s<br/>Password: %s</html>", userName,
                new String(password));
        JOptionPane.showMessageDialog(null, message);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

关于java - 如何在其他 JTextField 下找到 JTextField?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45402214/

相关文章:

java - JEdi​​torPane - 切换回默认编辑器工具包

user-interface - 使 SliverAppBar 将图像作为背景而不是颜色

java - 将多个矩形绘制到 JPanel

Java控制if else语句

java - 有没有比几个 if 语句更有效的方法来处理按钮点击事件?

java - Android - 最多每 15 分钟调用一个方法,否则使用保存的数据

java - Java Swing 中 JTable 对应的 WPF .NET 组件是什么?

java - 添加到ArrayList后刷新JTable

Java 'getLocalName()' 即使使用 'setNamespaceAware(true)' 也会返回 null

.net - 对轻量级 .net Win Forms HTML 渲染器控件有什么建议吗?