Search code examples
javaswingjbuttonactionlistenerclasscastexception

Error when using ActionListener button click to hide a JFrame and reveal another


I'm trying to "open" a different JFrame window on button click (in this case, the dimensions button), like when going through a menu. I have my 2 windows, Main function and ActionListener as separate classes. When i click on the button that is supposed to redirect me to the other window, it simply shows an error, and doesn't hide the first window and reveal the second one. The error is:

"Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class javax.swing.JButton cannot be cast to class javax.swing.JFrame (javax.swing.JButton and javax.swing.JFrame are in module java.desktop of loader 'bootstrap') at ActionDimensions.actionPerformed(ActionDimensions.java:8) at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972) at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313) at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405) at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262) at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279) at java.desktop/java.awt.Component.processMouseEvent(Component.java:6626) at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389) at java.desktop/java.awt.Component.processEvent(Component.java:6391) at java.desktop/java.awt.Container.processEvent(Container.java:2266) at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001) at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324) at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833) at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948) at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575) at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516) at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310) at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780) at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833) at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97) at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:746) at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:744) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:743) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) "

First window:

public class MainMenu extends JFrame {
   public MainMenu(){
       setSize(300,600);
       JPanel center = new JPanel();
       center.setLayout(new GridLayout(3,1));
       JButton MainMenuButtonStart = new JButton("Start");
       JButton MainMenuButtonContinue = new JButton("Continue");
       JButton MainMenuButtonDimensions = new JButton("Dimensions");
       center.add(MainMenuButtonStart);
       center.add(MainMenuButtonContinue);
       center.add(MainMenuButtonDimensions);
       ActionDimensions actionDimensions = new ActionDimensions();
       MainMenuButtonDimensions.addActionListener(actionDimensions);
       add(center, BorderLayout.CENTER);

   }
}

Second window:

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

public class Dimensions extends JFrame {
   public Dimensions(){
       setSize(600,500);
       JPanel north = new JPanel();
       JLabel labelnorth = new JLabel("Enter Dimensions! Rows and Columns");
       north.add(labelnorth);
       JPanel center = new JPanel();
       JPanel south = new JPanel();
       JButton buttonsouth = new JButton("Return to Main Menu");
       south.add(buttonsouth);
       center.setLayout(new GridLayout(1,2));
       TextField rowtextfield = new TextField();
       TextField columntextfield = new TextField();
       center.add(rowtextfield);
       center.add(columntextfield);
       add(north,BorderLayout.NORTH);
       add(center,BorderLayout.CENTER);
       add(south,BorderLayout.SOUTH);

Main function:

public class Main {
   public static void main(String[] args) {
       MainMenu mainMenu = new MainMenu();
       Dimensions dimensions = new Dimensions();
       dimensions.setVisible(false);
       mainMenu.setVisible(true);
       mainMenu.setDefaultCloseOperation(3);

   }
}

The action listener:

public class ActionDimensions implements ActionListener {
   @Override
   public void actionPerformed(ActionEvent e) {
   JFrame mainMenu = (JFrame) e.getSource();
   JFrame dimensions = (JFrame) e.getSource();
   mainMenu.setVisible(false);
   dimensions.setVisible(true);
   }
}

I think the problem is that i'm trying to use the getsource() method through a frame, but otherwise i can't access the instances (mainmenu and dimensions) if i don't specify that method. If that is the case, what other way can i connect the setVisible methods to the instances inside the ActionListener?


Solution

  • Here is likely your problem:

    JFrame mainMenu = (JFrame) e.getSource();
    JFrame dimensions = (JFrame) e.getSource();
    

    The ActionEvent#getSource() method returns the source that triggers the action, here a JButton, and you are trying to cast the same JButton object returned from this method as two different JFrames, which really doesn't make much sense, both because, again, the method doesn't return a JFrame and secondly, casting doesn't convert an object into two different objects. If you need a reference to the JFrame, pass it to where it is needed, such as by giving your ActionListener class a constructor that takes a JFrame parameter:

    ActionDimensions actionDimensions = new ActionDimensions(this);
    

    and

    public class ActionDimensions implements ActionListener {
        private  MainMenu mainMenu;
        
        public ActionDimensions(MainMenu mainMenu) {
            this.mainMenu = mainMenu;
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            // JFrame mainMenu = (JFrame) e.getSource();
            // JFrame dimensions = (JFrame) e.getSource();
            mainMenu.setVisible(false);
            
            // create a new Dimensions JFrame here?
            Dimensions dimensions = new Dimensions();
            // .... other code
            
            dimensions.setVisible(true);
        }
    }
    

    Alternatively, you can get the JFrame that the button sits on by calling SwingUtilities.getWindowAncestor(yourJButton).

    Having said this, understand that no user is going to enjoy having multiple windows thrust at them, which is what swapping JFrames does, and there are much better ways of cleanly swapping views, such as with a CardLayout tutorial. Please read The Use of Multiple JFrames, Good/Bad Practice?.