【问题标题】:How to locate JTextField under other JTextField?如何在其他 JTextField 下找到 JTextField?
【发布时间】:2018-01-06 05:14:59
【问题描述】:

我正在制作身份验证 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(并排的字段,居中):

【问题讨论】:

  • 这就是 FlowLayout 所做的——将一个组件放在另一个旁边。使用不同的布局。 GridBagLayout 在这里或嵌套布局都可以很好地工作。
  • 你可以看看如何使用stackoverflow.com/questions/19415170/…setbounds
  • GridBagLayout 结果如下:sc.reflex.rip/FwJVkM.png 没有太大变化。字段只是垂直居中。我知道如何使用 setBounds,而且我使用正确。
  • 1) 提示:添加@HovercraftFullOfEels(或重要的@)以通知此人有新评论。 2) "GridBagLayout 结果是这样的:" 代码显然是错误的。最好调试一下。发布该尝试的minimal reproducible example“我知道如何使用 setBounds,而且我正确地使用它。” 使用 null 布局的唯一“正确”方式是不要(使用它们)。
  • 您的 GridBagLayout 图片表明您正在添加组件而没有传入 GridBagConstraints,这表明您正在使用复杂的布局而没有先查看 GridBagLayout tutorial。帮自己一个忙,看看那个教程,然后使用约束。

标签: java swing user-interface layout layout-manager


【解决方案1】:

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

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

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));

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

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());
    }
}

【讨论】:

  • 有没有办法完全禁用自动调整大小和定位?所有的布局似乎都不适合我想要的,我更愿意自己计算一切。
  • @HopeIsNope:是的,您可以将布局设置为null,但我敦促您不要这样做。让布局为您完成工作要好得多。您使用 null 布局和 setBounds 创建了一个复杂布局的图像,它在您的计算机上看起来很棒。然后一位朋友尝试了您的程序,但在他们的计算机上,按钮名称没有完全显示——这是由于屏幕分辨率不同导致的常见问题。或者稍后您想在其中一组中再添加一个 JRadioButton,现在您发现必须重新计算要更改的区域右侧和下方所有组件的位置和大小
  • @HopeIsNope:试图维护这些东西是一场噩梦。如果您学习了布局管理器,并且学得很好,那么 GUI 就会自然而然地到位。
  • 能否请您链接一些好的布局教程/解释?
  • 我的荣幸:layout manager tutorial。还可以查看来自 Swing 专家的关于该主题的帖子,您可以找到 here。尤其要查找 Camickr、MadProgrammer 和 AndrewThompson 的帖子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-11
  • 2022-01-10
  • 2016-07-26
  • 2014-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多