JFrame 或 JPanel 不监听点击事件

The JFrame or the JPanel don't listen to the clicking event

我有这个 JFrame(BorderLayout),其中有 3 个 Jpanel(北、中、南)。 中心面板,有一个登录按钮。 出于测试目的,我想要的是在按下时打印“hello”。

框架的当前外观如下:

我试过两种方法:

  1. 在中心面板上“实现了 ActionListener”,但没有任何反应,甚至没有错误。

  2. 在框架本身上“实现 ActionListener”,但是,我遇到了问题(更像是,我不知道如何)“导入”/访问按钮,所以JFrame可以监听点击事件

希望你能帮助我。

这是代码:

主程序:

package com.system.reservation.airline;

public class Main {
    public static void main(String[] args) {
        LoginPage loginPage = new LoginPage(); 
    }
}

登录页面:

import com.system.reservation.airline.panels.BodyPanel;
import com.system.reservation.airline.panels.FooterPanel;
import com.system.reservation.airline.panels.HeaderPanel;

import javax.swing.*;
import java.awt.*;

public class LoginPage extends JFrame{ 
    HeaderPanel headerPanel = new HeaderPanel();
    BodyPanel bodyPanel = new BodyPanel();
    FooterPanel footerPanel = new FooterPanel();

    public LoginPage(){ // Constructor
        this.setTitle("Login");
        this.setSize(400, 600);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        this.getContentPane().setBackground(new Color(240,248,255));
        this.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();

        gbc.weightx = 1;
        gbc.weighty = 1;
        gbc.fill = GridBagConstraints.BOTH;

        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridheight = 1;
        this.add(headerPanel, gbc);

        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.gridheight = 1;
        this.add(bodyPanel, gbc);

        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.gridheight = 3;
        this.add(footerPanel, gbc);

        this.pack();
        this.setVisible(true);
    }
}

正文面板:

package com.system.reservation.airline.panels;

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

public class BodyPanel extends JPanel implements ActionListener{
    JLabel login_label;
    JButton login_button = new JButton("Login");
    Color transparent = new Color(1f,0f,0f,0f);

    public BodyPanel(){
        this.setBackground(transparent);
        this.setLayout(new BorderLayout());
        this.setBorder(BorderFactory.createLineBorder(Color.green, 3));
        //------------ Login Panel ------------------------
        JPanel login_panel = new JPanel(new BorderLayout());
        login_panel.setBackground(transparent);
        login_panel.setPreferredSize(new Dimension(400, 100));
        //login_panel.setBorder(BorderFactory.createLineBorder(Color.YELLOW, 5));

        login_label = new JLabel();
        login_label.setText("Login");
        login_label.setHorizontalAlignment(JLabel.CENTER);
        login_label.setFont(new Font("Arial", Font.PLAIN, 20));

        login_panel.add(login_label, BorderLayout.NORTH);
        //------------ Login Panel ------------------------

        //------------ Input Panel ------------------------
        JPanel input_fields_panel = new JPanel(new GridBagLayout());
        input_fields_panel.setBackground(transparent);
        input_fields_panel.setPreferredSize(new Dimension(400, 150));
        //input_fields_panel.setBorder(BorderFactory.createLineBorder(Color.black, 5));

        GridBagConstraints gbc = new GridBagConstraints();

        gbc.weightx = 1;
        gbc.weighty = 1;
        gbc.fill = GridBagConstraints.BOTH;

        //-------------
        JLabel userName_label = new JLabel("Username: ");
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        gbc.insets = new Insets(0,10,0,0);
        input_fields_panel.add(userName_label, gbc);

        JTextField userName_txb = new JTextField();
        userName_txb.setPreferredSize(new Dimension(250,30));
        gbc.gridx = 3;
        gbc.insets = new Insets(0,0,5,20);
        input_fields_panel.add(userName_txb, gbc);
        //-------------

        //-------------
        JLabel password_label = new JLabel("Password: ");
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        gbc.insets = new Insets(0,10,0,0);
        input_fields_panel.add(password_label, gbc);

        JTextField password_txb = new JTextField();
        password_txb.setPreferredSize(new Dimension(250,30));
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.gridwidth = 3;
        gbc.gridheight = 1;
        gbc.insets = new Insets(5,0,5,20);
        input_fields_panel.add(password_txb, gbc);
        //-------------

        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.gridwidth = 4;
        gbc.gridheight = 1;
        gbc.insets = new Insets(5,150,5,150);
        input_fields_panel.add(login_button, gbc);

        JLabel tokenResponse = new JLabel("Username or password, are incorrect!");
        tokenResponse.setHorizontalAlignment(JLabel.CENTER);
        gbc.gridx = 0;
        gbc.gridy = 3;
        gbc.gridwidth = 4;
        gbc.gridheight = 1;
        gbc.insets = new Insets(0,0,0,0);
        input_fields_panel.add(tokenResponse, gbc);
        //------------ Input Panel ------------------------

        //-------------------------------------
        this.add(login_panel, BorderLayout.NORTH);
        this.add(input_fields_panel, BorderLayout.CENTER);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == login_button) {
            System.out.println("Hello");
        }
    }
}

中间面板是 BodyPanel,因此我没有包含 HeaderPanel 或 FooterPanel 的代码。

总而言之,我想让 LoginPage 能够访问或监听 login_button 的点击事件,或者 BodyPanel 完全相同。

我想你忘了将 ActionListener 接口的实现添加到你的按钮。

在你的构造函数中,在某些时候应该有这样的东西:

public BodyPanel(){
   //...
   login_button.addActionListener(this);
   //...
}

“明显”的问题是您没有将 ActionListener 注册到 login_button。不太“明显”的问题是,当 actionPerformed 方法被调用时,你用它做什么。

这就是 observer pattern 变得重要的地方(您已经使用过,ActionListener 是一种观察者模式)。

您需要以某种方式知道 BodyPanel 何时对用户进行了身份验证。为此,我将向 BodyPanel 添加某种“登录观察者”,例如 LoginPage 将向其注册。

public interface LoginObserver {
    public void didAuthenticate(BodyPanel source, User user);
}

我可能还会考虑使用 Dependency Injection 来定义某种“身份验证”工作流程,它将通过 LoginPage 传递到 BodyPanel 以分离工作流程,但这可能会超出范围。

可运行示例

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new LoginPage();
            }
        });
    }

    public interface User {
        public String getName();
    }

    public class DefaultUser implements User {
        private String name;

        public DefaultUser(String name) {
            this.name = name;
        }

        @Override
        public String getName() {
            return name;
        }
    }

    public class LoginPage extends JFrame {
//        HeaderPanel headerPanel = new HeaderPanel();
        BodyPanel bodyPanel = new BodyPanel(new BodyPanel.LoginObserver() {
            @Override
            public void didAuthenticate(BodyPanel source, User user) {
                JOptionPane.showMessageDialog(getContentPane(), "Welcome " + user.getName(), "Welcome", JOptionPane.PLAIN_MESSAGE);
            }
        });
//        FooterPanel footerPanel = new FooterPanel();

        public LoginPage() { // Constructor
            this.setTitle("Login");
            this.setSize(400, 600);
            this.setLocationRelativeTo(null);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.getContentPane().setBackground(new Color(240, 248, 255));
            this.setLayout(new GridBagLayout());

            GridBagConstraints gbc = new GridBagConstraints();

            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;

            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.gridheight = 1;
//            this.add(headerPanel, gbc);

            gbc.gridx = 0;
            gbc.gridy = 1;
            gbc.gridheight = 1;
            this.add(bodyPanel, gbc);

            gbc.gridx = 0;
            gbc.gridy = 2;
            gbc.gridheight = 3;
//            this.add(footerPanel, gbc);

            this.pack();
            this.setVisible(true);
        }
    }

    public class BodyPanel extends JPanel implements ActionListener {
        private interface LoginObserver {
            public void didAuthenticate(BodyPanel source, User user);
        }

        JLabel login_label;
        JButton login_button = new JButton("Login");
        // This is VERY bad idea
        //Color transparent = new Color(1f, 0f, 0f, 0f);

        private JTextField userName_txb;
        private JPasswordField password_txb;
        private JLabel tokenResponse;

        private LoginObserver loginObserver;

        public BodyPanel(LoginObserver loginObserver) {
            this.loginObserver = loginObserver;
            //this.setBackground(transparent);
            setOpaque(false);
            this.setLayout(new BorderLayout());
            this.setBorder(BorderFactory.createLineBorder(Color.green, 3));
            //------------ Login Panel ------------------------
            JPanel login_panel = new JPanel(new BorderLayout());
//            login_panel.setBackground(transparent);
            login_panel.setOpaque(false);
            // This is another BAD idea
//            login_panel.setPreferredSize(new Dimension(400, 100));
            //login_panel.setBorder(BorderFactory.createLineBorder(Color.YELLOW, 5));

            login_label = new JLabel();
            login_label.setText("Login");
            login_label.setHorizontalAlignment(JLabel.CENTER);
            login_label.setFont(new Font("Arial", Font.PLAIN, 20));

            login_panel.add(login_label, BorderLayout.NORTH);
            //------------ Login Panel ------------------------

            //------------ Input Panel ------------------------
            JPanel input_fields_panel = new JPanel(new GridBagLayout());
//            input_fields_panel.setBackground(transparent);
            input_fields_panel.setOpaque(false);
            // This is another BAD idea
//            input_fields_panel.setPreferredSize(new Dimension(400, 150));
            //input_fields_panel.setBorder(BorderFactory.createLineBorder(Color.black, 5));

            GridBagConstraints gbc = new GridBagConstraints();

            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;

            //-------------
            JLabel userName_label = new JLabel("Username: ");
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.gridwidth = 1;
            gbc.gridheight = 1;
            gbc.insets = new Insets(0, 10, 0, 0);
            input_fields_panel.add(userName_label, gbc);

            userName_txb = new JTextField(10);
            //userName_txb.setPreferredSize(new Dimension(250, 30));
            gbc.gridx = 3;
            gbc.insets = new Insets(0, 0, 5, 20);
            input_fields_panel.add(userName_txb, gbc);
            //-------------

            //-------------
            JLabel password_label = new JLabel("Password: ");
            gbc.gridx = 0;
            gbc.gridy = 1;
            gbc.gridwidth = 1;
            gbc.gridheight = 1;
            gbc.insets = new Insets(0, 10, 0, 0);
            input_fields_panel.add(password_label, gbc);

            password_txb = new JPasswordField(10);
//            password_txb.setPreferredSize(new Dimension(250, 30));
            gbc.gridx = 1;
            gbc.gridy = 1;
            gbc.gridwidth = 3;
            gbc.gridheight = 1;
            gbc.insets = new Insets(5, 0, 5, 20);
            input_fields_panel.add(password_txb, gbc);
            //-------------

            gbc.gridx = 0;
            gbc.gridy = 2;
            gbc.gridwidth = 4;
            gbc.gridheight = 1;
            gbc.insets = new Insets(5, 150, 5, 150);
            input_fields_panel.add(login_button, gbc);

            login_button.addActionListener(this);

            tokenResponse = new JLabel("Username or password, are incorrect!");
            tokenResponse.setHorizontalAlignment(JLabel.CENTER);
            gbc.gridx = 0;
            gbc.gridy = 3;
            gbc.gridwidth = 4;
            gbc.gridheight = 1;
            gbc.insets = new Insets(0, 0, 0, 0);
            input_fields_panel.add(tokenResponse, gbc);
            //------------ Input Panel ------------------------

            //-------------------------------------
            this.add(login_panel, BorderLayout.NORTH);
            this.add(input_fields_panel, BorderLayout.CENTER);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == login_button) {
                // Perform authentication via a seperate service
                loginObserver.didAuthenticate(this, new DefaultUser(userName_txb.getText()));
            }
        }
    }
}

备注

在 Swing 组件上使用透明颜色 (Color transparent = new Color(1f, 0f, 0f, 0f);) 将导致无穷无尽的问题。 Swing 组件要么完全不透明,要么完全透明,这是通过 opaque 属性.

控制的

我会避免使用 setPreferredSize,同样,这只会给您带来无穷无尽的问题。利用布局(GridBagLayoutConstraints 提供许多属性,可以帮助调整组件的间距和大小),EmptyBorders,对于文本组件,通过 columns 调整大小提示属性