Search code examples
javaswingmodel-view-controllerjbuttonobserver-pattern

How to observe a JButton from a different class?


So I have a class that extends a JPanel and within the constructor I add my JButtons and whatever else I need to add. I also have a MainFrame class that is the container (JFrame) and this class will take an argument from a class called FrameSwitcher (Controller) which will assess what buttons were clicked, and pass the information to the MainFrame

I'm having troubles doing this, I can't find a proper way to do this. I do also wish to maintain the JButtons private and non static.

JPanel example:

public class MainMenu() {

    private JButton btnSinglePlayer, btnMultiPlayer;

    public MainMenu() {
        setLayout(null);

        btnSinglePlayer = new JButton("singlePlayer");
        btnSinglePlayer.setBounds(320, 25, 275, 130);
        add(btnSinglePlayer);

        btnMultiPlayer = new JButton("MultiPlayer");
        btnMultiPlayer.setBounds(320, 170 , 275, 130);
        add(btnMultiPlayer);

   }
}

FrameSwitcher:

public class FrameSwitcher implements panelListener { // panelListener is an interface defined else where.

    public FrameSwitcher(MainFrame frame) {
        // This is irrelevant to the question. 
    } 

    @Override
    public void gamePanel() {
        System.out.println("GamePanel Event: Recieved");
    }
    @Override
    public void mainMenu() {
        System.out.println("mainMenu Event: Recieved");
    }
    @Override
    public void scoreBoardPanel() {
        System.out.println("scoreBoardPanel Event: Recieved");
    }
}

Then my MainFrame:

public class MainFrame extends JFrame implements ActionListener {

    private PanelListener panelListener;
    private JFrame mainContainer = new JFrame("Game");
    private JPanel mainMenu = new MainMenu();

    public void start() {
         mainContainer(mainMenu);
    }

    public MainFrame(JPanel frame) {
        mainContainer.getContentPane().add(frame);
        mainContainer.pack();
        // Other methods to initialize the frame
        return mainContainer;
     }

    public void switchFrames(PanelListener panelListener) {
        this.panelListener = panelListener; // PanelListener is an interface.
    }

    public void actionPerformed(ActionEvent e) {

        JButton source = (JButton)e.getsource();

        if(source == MainMenu.btnSinglePlayer) {
            if(panelListener != null) {
                System.out.println("Recieved the event approriately.");
            }
        }
    }
}

In this example, it does compile, but doesn't do what it is supposed to. Another thing is I currently have the JButtons as public and static, I don't want that.


Solution

  • In your MainMenu class, you need to add some kind of listener that interested parties can register with, so when some event occurs, they can be notified.

    The simplest solution would be to provide a addActionListener method which delegated to each of the buttons. This, however, has may expose portions of the application you don't exposed (a listener now has direct access to the JButton and can do all kinds of nasty things to it).

    A better solution would be to create something like a MainMenuListener which had methods like startSinglePlayer and startMultiPlayer

    You would then provide a add/removeMainMenuListener method within in your MainMenu class.

    Each button would then register there own actionListener and fire the appropriate menu listener event

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.EventListener;
    import javax.swing.JButton;
    import javax.swing.JPanel;
    
    public class MainMenu extends JPanel {
    
        private JButton btnSinglePlayer, btnMultiPlayer;
    
        public MainMenu() {
            setLayout(null);
    
            btnSinglePlayer = new JButton("singlePlayer");
            btnSinglePlayer.setBounds(320, 25, 275, 130);
            add(btnSinglePlayer);
            btnSinglePlayer.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    fireStartSinglePlayer();
                }
            });
    
            btnMultiPlayer = new JButton("MultiPlayer");
            btnMultiPlayer.setBounds(320, 170, 275, 130);
            add(btnMultiPlayer);
            btnMultiPlayer.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    fireStartMultiPlayer();
                }
            });
    
        }
    
        public void addMainMenuListener(MainMenuListener listener) {
            listenerList.add(MainMenuListener.class, listener);
        }
    
        public void removeMainMenuListener(MainMenuListener listener) {
            listenerList.remove(MainMenuListener.class, listener);
        }
    
        public void fireStartSinglePlayer() {
            MainMenuListener[] listeners = listenerList.getListeners(MainMenuListener.class);
            if (listeners != null && listeners.length > 0) {
                for (MainMenuListener listener : listeners) {
                    listener.startSinglePlayer();
                }
            }
        }
    
        public void fireStartMultiPlayer() {
            MainMenuListener[] listeners = listenerList.getListeners(MainMenuListener.class);
            if (listeners != null && listeners.length > 0) {
                for (MainMenuListener listener : listeners) {
                    listener.startMultiPlayer();
                }
            }
        }
    
        public interface MainMenuListener extends EventListener {
    
            public void startSinglePlayer();
            public void startMultiPlayer();
    
        }
    
    }