如何在不关闭另一个 JFrame window 的情况下关闭它?

How do you close a JFrame window without closing another one?

我是 Java Swing 的新手,我正在尝试学习如何在不使用按钮关闭另一帧的情况下关闭一帧。例如,我有一个 frame1/window,它只有一个名为登录的按钮。单击登录按钮后,另一个 window 出现 frame2。在 frame2 上,我只有一个示例 JLabel“Hello And Welcome”,名为注销的按钮。我希望能够单击 frame2 上的注销按钮,frame2 window 应该关闭,但 frame1 window 显示仍然打开。我已经尝试了 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE),但它只有在我单击 frame2 window 右上角的 x 图标时才有效。有谁知道单击按钮时关闭框架的方法吗?

public class Frame1 extends JFrame implements ActionListener{
    
    private static JButton login = new JButton("Login"); 
    private static JFrame f = new JFrame("Login");  

    Frame1(){
     
        f.setSize(1000,750);
        f.setLocation(750, 250);

        login.setBounds(250, 350, 150, 30);

        f.add(login);
        f.setLayout(null);    
        f.setVisible(true); 
        login.addActionListener(this);

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }

    public void actionPerformed(ActionEvent e){

        if (e.getSource() == login){

            Frame2.frame2windown();
        }
    }

    public static void main(String [] args){

        Frame1 login1 = new Frame1();
    }
}

public class Frame2 extends JFrame implements ActionListener{

    private static JButton logout = new JButton("Logout"); 
    private static JLabel jb1 = new JLabel ("Hello And Welcome");
    private static JFrame f = new JFrame("Log Out");  

    Frame2(){
     

        f.setSize(1000,750);
        f.setLocation(750, 250);

        jb1.setBounds(250, 150, 350, 30);

        logout.setBounds(250, 350, 150, 30);

        f.add(logout);
        f.add(jb1);
        f.setLayout(null);    
        f.setVisible(true); 

        logout.addActionListener(this);

        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    }

    public void actionPerformed(ActionEvent a){

        if(a.getSource() == logout){

            dispose();
            WindowEvent closeWindow = new WindowEvent(this, JFrame.DISPOSE_ON_CLOSE);
            Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(closeWindow);

        }

    }


    public static void frame2windown(){
        
        Frame2 f2 = new Frame2();

    }
}

因此,您需要尝试和学习一大堆概念。

  1. 通常建议不要从顶级容器扩展(如 JFrame)。您也没有添加任何新功能;它们是复杂的复合组件;您将自己锁定在一个用例中(如果您想将 UI 包含在另一个 UI 中或使用对话框而不是框架会怎样?!)
  2. 多帧并不总是一个好主意,而且可能会让用户感到困惑。一般来说,对于登录工作流程,我可能认为登录对话框通常是更好的解决方案,但您需要了解用例才能做出这些决定。
  3. Swing 是一个庞大、丰富且多样化的 API,它有很多内置功能,您可以使用这些功能来让您的生活更轻松(尽管看起来并不总是这样)

布局管理器是绝对必需的功能,您确实需要花时间学习它们,请参阅 Laying Out Components Within a Container 了解更多详细信息。

所以,这是一个使用 CardLayout 和基本“观察者模式”的非常简单的示例,它解耦并分离了责任。

import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Test {
    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new NavigationPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class NavigationPane extends JPanel {

        protected enum NavigationTarget {
            LOGIN, MAIN;
        }

        private LoginPane loginPane;
        private MainPane mainPane;

        private CardLayout cardLayout;

        public NavigationPane() {
            cardLayout = new CardLayout();
            setLayout(cardLayout);

            loginPane = new LoginPane();
            loginPane.addLoginListener(new LoginPane.LoginListener() {
                @Override
                public void loginDidFail(LoginPane source) {
                    JOptionPane.showMessageDialog(NavigationPane.this, "You are not unauthroised", "Error", JOptionPane.ERROR_MESSAGE);
                }

                @Override
                public void loginWasSuccessful(LoginPane source) {
                    navigateTo(NavigationTarget.MAIN);
                }
            });

            mainPane = new MainPane();

            add(loginPane, NavigationTarget.LOGIN.name());
            add(mainPane, NavigationTarget.MAIN.name());

            navigateTo(NavigationTarget.LOGIN);
        }

        protected void navigateTo(NavigationTarget target) {
            cardLayout.show(this, target.name());
        }
    }

    public static class LoginPane extends JPanel {

        public static interface LoginListener extends EventListener {
            public void loginDidFail(LoginPane source);
            public void loginWasSuccessful(LoginPane source);
        }

        public LoginPane() {
            setBorder(new EmptyBorder(10, 10, 10, 10));
            setLayout(new GridBagLayout());
            JButton btn = new JButton("Login");
            btn.addActionListener(new ActionListener() {
                private Random rnd = new Random();

                @Override
                public void actionPerformed(ActionEvent e) {
                    // Do some logic here
                    if (rnd.nextBoolean()) {
                        fireLoginWasSuccessful();
                    } else {
                        fireLoginDidFail();
                    }
                }
            });
            add(btn);
        }

        public void addLoginListener(LoginListener listener) {
            listenerList.add(LoginListener.class, listener);
        }

        public void removeLoginListener(LoginListener listener) {
            listenerList.remove(LoginListener.class, listener);
        }

        protected void fireLoginDidFail() {
            LoginListener[] listeners = listenerList.getListeners(LoginListener.class);
            for (LoginListener listener : listeners) {
                listener.loginDidFail(this);
            }
        }

        protected void fireLoginWasSuccessful() {
            LoginListener[] listeners = listenerList.getListeners(LoginListener.class);
            for (LoginListener listener : listeners) {
                listener.loginWasSuccessful(this);
            }
        }

    }

    public static class MainPane extends JPanel {

        public MainPane() {
            setLayout(new GridBagLayout());
            setBorder(new EmptyBorder(10, 10, 10, 10));
            add(new JLabel("Welcome"));
        }

    }
}

JDialog 基于登录流程

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                NavigationPane navigationPane = new NavigationPane();
                JFrame frame = new JFrame();
                frame.add(navigationPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                if (LoginPane.showLoginDialog(navigationPane)) {
                    navigationPane.didLogin();
                } else {
                    frame.dispose();
                }
            }
        });
    }

    public static class NavigationPane extends JPanel {

        protected enum NavigationTarget {
            SPLASH, MAIN;
        }

        private SplashPane splashPane;
        private MainPane mainPane;

        private CardLayout cardLayout;

        public NavigationPane() {
            cardLayout = new CardLayout();
            setLayout(cardLayout);

            mainPane = new MainPane();
            splashPane = new SplashPane();

            add(splashPane, NavigationTarget.SPLASH.name());
            add(mainPane, NavigationTarget.MAIN.name());

            navigateTo(NavigationTarget.SPLASH);
        }

        protected void navigateTo(NavigationTarget target) {
            cardLayout.show(this, target.name());
        }

        public void didLogin() {
            navigateTo(NavigationTarget.MAIN);
        }
    }

    public static class LoginPane extends JPanel {

        private Random rnd = new Random();
        private boolean isAuthorised = false;

        public LoginPane() {
            setBorder(new EmptyBorder(10, 10, 10, 10));
            setLayout(new GridBagLayout());

            add(new JLabel("User name and password fields go here"));
        }

        protected void authenticate() {
            // Authenticate
            isAuthorised = rnd.nextBoolean();
            if (!isAuthorised) {
                JOptionPane.showMessageDialog(this, "You are not authorised", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }

        // So this should return some kind of "session" or something so
        // can identify the user, but for now, we'll just use
        // a boolean
        public boolean isAuthorised() {
            return isAuthorised;
        }

        public static boolean showLoginDialog(Component parent) {
            LoginPane loginPane = new LoginPane();
            JPanel panel = new JPanel(new BorderLayout());
            JPanel buttonPane = new JPanel(new GridBagLayout());

            JButton okayButton = new JButton("Login");
            JButton cancelButton = new JButton("Cancel");

            buttonPane.add(okayButton);
            buttonPane.add(cancelButton);

            panel.add(loginPane);
            panel.add(buttonPane, BorderLayout.SOUTH);

            JDialog dialog = new JDialog(SwingUtilities.windowForComponent(parent));
            dialog.add(panel);

            okayButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    loginPane.authenticate();
                    if (loginPane.isAuthorised()) {
                        dialog.dispose();
                    }
                }
            });
            cancelButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    dialog.dispose();
                }
            });
            dialog.setModal(true);
            dialog.pack();
            dialog.setLocationRelativeTo(parent);
            dialog.setVisible(true);

            return loginPane.isAuthorised();
        }

    }

    public static class SplashPane extends JPanel {

        public SplashPane() {
            setLayout(new GridBagLayout());
            setBorder(new EmptyBorder(10, 10, 10, 10));
            add(new JLabel("This is a splash panel, put some nice graphics here"));
        }

    }

    public static class MainPane extends JPanel {

        public MainPane() {
            setLayout(new GridBagLayout());
            setBorder(new EmptyBorder(10, 10, 10, 10));
            add(new JLabel("Welcome"));
        }

    }
}

您复制了 JFrame,在 JFrame 中创建了一个 JFrame 字段 f。 不要使用像按钮这样的静态组件。

public class Frame1 extends JFrame implements ActionListener {

    private final JButton login = new JButton("Login");

    Frame1() {
        setTitle("Login");
        setSize(1000, 750);
        setLocation(750, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(null);

        login.setBounds(250, 350, 150, 30);

        add(login);
        login.addActionListener(this);
        setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == login) {
            Frame2.frame2windown();
        }
    }

    public static void main(String[] args) {
       EventQueue.invokeLater(() -> {
          Frame1 login1 = new Frame1();
       }
    }
}

在此线程上使用 swing/awt 事件队列 (invokeLater) window 进一步处理和分派事件。

和第 2 帧:

public class Frame2 extends JFrame implements ActionListener {

    private JButton logout = new JButton("Logout");
    private JLabel jb1 = new JLabel("Hello And Welcome");

    Frame2() {
        setTitle("Logout");
        setSize(1000, 750);
        setLocation(750, 250);
        setLayout(null);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        jb1.setBounds(250, 150, 350, 30);
        logout.setBounds(250, 350, 150, 30);
        add(logout);
        add(jb1);
        logout.addActionListener(this);
        setVisible(true);
    }

    public void actionPerformed(ActionEvent a) {
        if (a.getSource() == logout) {
            setVisible(false); // <--- ALL
        }
    }

    public static void frame2windown() {
        Frame2 f2 = new Frame2();
    }
}

JFrame.setVisible 做到了这一切。特别是 setVisible(true) 甚至应该在调用构造函数之后完成,所以它总是最后一个。

另一句话,快速进入布局管理器。绝对布局 (null) 是一个 PITA。