Java: JPanel 组件在切换面板时向一侧倾斜

Java: JPanel components tilt to the side when switching panels

我想使用 BorderLayout(即使我可以尝试 CardLayout,如果有必要)和固定组件位置以供个人将来参考。

在我的示例中,我创建了三个面板:

如果登录信息正确,程序切换到home_page

如果登录信息不正确,程序切换到exit_page

我测试了所有可能的面板序列。当我尝试从第一个面板转到下一个面板时,下一个面板的组件似乎被拖到一边,如下所示:

当我尝试在 actionPerformed 方法的末尾再次设置布局时,我相信我遗漏了一些东西,但我在网上找到的几个例子都使用了这种方法。

如何将下一个面板的组件放到正确的位置?

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class LoginForm extends JFrame implements ActionListener
{
    private JFrame frame;                               //main frame

    private JPanel login_page;                          //3 different panels/pages/states
    private JPanel home_page;
    private JPanel exit_page;

    private JLabel login_label;                         //login form components
    private JLabel username_label;
    private JLabel password_label;
    private JTextField username_text;
    private JPasswordField password_text;
    private JButton login_button;

    private JLabel home_label;                          //homepage components
    private JButton logout_button;

    private JLabel locked_label;                        //exit page components
    private JButton exit_button;

    LoginForm()                                         //initialization for the components and panels
    {                                                   
       frame = new JFrame("Login Form frame");
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


       //////////////////////////////////////////////////////
       /////////login form components initialization/////////
       //////////////////////////////////////////////////////
       login_page = new JPanel();

        login_label = new JLabel("Login label");

        username_label = new JLabel("Username");
        password_label = new JLabel("Password");

        username_text = new JTextField();
        password_text = new JPasswordField();

        login_button = new JButton("Login");
        login_button.addActionListener(this);


        login_label.setBounds(100,30,400,30);
        username_label.setBounds(80,70,200,30);
        username_text.setBounds(300,70,200,30);
        password_label.setBounds(80,110,200,30);
        password_text.setBounds(300,110,200,30);
        login_button.setBounds(150,160,100,30);


        login_page.add(login_label);
        login_page.add(username_label);
        login_page.add(username_text);
        login_page.add(password_label);
        login_page.add(password_text);
        login_page.add(login_button);
       //////////////////////////////////////////////////////
       //////////////////////////////////////////////////////
       //////////////////////////////////////////////////////


       //////////////////////////////////////////////////////
       //////////home page components initialization/////////
       //////////////////////////////////////////////////////
       home_page = new JPanel();


        home_label = new JLabel("Home label");

        logout_button = new JButton("Logout");
        logout_button.addActionListener(this);

        home_label.setBounds(100,30,400,30);
        logout_button.setBounds(150,160,100,30);

        home_page.add(home_label);
        home_page.add(logout_button);

       //////////////////////////////////////////////////////
       //////////////////////////////////////////////////////
       //////////////////////////////////////////////////////

       //////////////////////////////////////////////////////
       //////////exit page components initialization/////////
       ////////////////////////////////////////////////////// 
       exit_page = new JPanel();

        locked_label = new JLabel("You are now locked from the database");

        exit_button = new JButton("Exit");
        exit_button.addActionListener(this);

        locked_label.setBounds(100,30,400,30);
        exit_button.setBounds(150,160,100,30);

        exit_page.add(locked_label);
        exit_page.add(exit_button);
       //////////////////////////////////////////////////////
       //////////////////////////////////////////////////////
       //////////////////////////////////////////////////////



       frame.setContentPane(login_page);        //first page to get seen
       frame.setSize(550,250);                  //size of the window
       frame.setLayout(new BorderLayout());
       frame.setVisible(true);  
       frame.setResizable(false);
    }

    @Override
    public void actionPerformed(ActionEvent listener)
    {

        JButton button = (JButton) listener.getSource();

        if(button == login_button)
        {
            String name = username_text.getText();
            String password = new String(password_text.getPassword());

                if(name.equals("x") && password.equals("x"))
            {
                frame.remove(login_page);
                frame.setContentPane(home_page);
            }
            else
            {
                frame.remove(login_page);
                frame.setContentPane(exit_page);
            }
        }
        else if(button == logout_button)
        {
            frame.remove(home_page);
            frame.setContentPane(login_page);
        }
        else if (button == exit_button)
        {
            frame.dispose();
        }

        frame.setLayout(new BorderLayout());
        frame.validate();
        frame.repaint();  
    }

    public static void main(String args[])
    {
        LoginForm login = new LoginForm();
    }
}

您的问题可能是frame.setSize(550,250);

在使用布局时,最好在添加所有组件和面板后使用 pack()

将所有 JPanels 的布局设置为空。默认情况下,将设置 FlowLayout,这是导致问题的原因。

初始化后添加login_page.setLayout(null)

初始化后添加home_page.setLayout(null)

初始化后添加exit_page.setLayout(null)

这应该可以解决您的问题。虽然这是解决问题的快速方法,但这不是正确的解决方法。

在 java swings 中设置空布局是不可取的,因为:

A layout manager makes it easier to adjust to look-and-feel-dependent component appearances, to different font sizes, to a container's changing size, and to different locales. Layout managers also can be reused easily by other containers, as well as other programs

所以总是使用布局管理器。因此,不要使用 setBounds() 语句手动设置组件的位置,而是使用布局管理器来定位组件。有关如何使用各种布局管理器的更多信息,请参阅 here。我更喜欢这里的 GridBagLayout,因为它更灵活。

but several examples I found online used this exact way.

我对此表示怀疑,因为代码有很多问题,它只能偶然运行。

让我们从 Swing 的设计用途开始:

  1. 每个面板都应该使用布局管理器。布局管理器将设置添加到面板的每个组件的大小和位置。框架内容窗格的默认布局管理器是 BorderLayout。 JPanel 的默认布局管理器是 FlowLayout.

  2. 编码的正常顺序是创建面板和设置布局管理器。然后将组件添加到面板。然后将面板添加到框架中。

  3. 接下来在框架上调用 pack() 和 setVisible()。这将为所有添加到框架的面板调用布局管理器,组件将根据每个布局管理器的规则逻辑显示。

  4. 现在随着框架的大小调整,组件将grow/shrink基于布局管理器的规则,您已经为自己创建了一个漂亮的动态 GUI。

现在你在做什么:

  1. 您正在尝试使用 setBounds() 来设置每个组件的大小和位置。大小只是一个随机猜测。你不知道合适的尺寸应该是多少。如果您想将字体更改为更大的尺寸怎么办?现在您将开始截断文本。不要尝试设置 Swing 组件的边界。每个组件都有逻辑来确定自己的首选大小。

  2. 接下来设置框架的内容窗格(这很好),然后调用 setLayout() 和 validate()。你甚至知道那些声明的作用吗?

  3. 使用 setLayout() 的目的是告诉面板在向框架添加组件时使用什么布局管理器,但绝不会向框架添加任何面板。

  4. 接下来使用 validate()(使用 Swing 时应该是 revalidate())。该方法的目的是调用布局管理器。因此布局管理器将重置所有组件的 size/location 并完全忽略您使用的 setBounds() 语句。

  5. 您的代码只是偶然起作用,因为您在将面板添加到框架后将布局更改为 BorderLayout。由于在使用 BorderLayout 时您永远不会向框架添加任何组件,因此布局管理器无事可做。

所以解决方案是:

  1. 不要使用 setBounds()。

  2. 为您的 3 个面板中的每一个使用适当的布局管理器。

  3. 在框架上使用CardLayout。然后您可以根据需要交换上述每个面板。不要试图重新发明轮子。

  4. 阅读 Layout Mangers 上的 Swing 教程,了解更多信息和工作示例。