ActionListeners JMenuItems - Cross classes

121 views Asked by At

I'm making a very poor attempt at ActionListeners here.

I'm trying to use ActionListeners to run code from in another class (AddForm.java) once a JMenuItem (in MainMenu.java) is clicked.

First, here's the code: MainMenu.java

package carparksystem;

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

public class MainMenu extends JFrame
{
public MainMenu()
{
    JMenuBar mainMenu = new JMenuBar();
    JMenu main = new JMenu("Menu");

    mainMenu.add(main);

    JMenuItem addCar = new JMenuItem("Add Car");
    addCar.setActionCommand("Add");
    main.add(addCar);
    //addCar.addActionListener();

    JMenuItem removeCar = new JMenuItem("Remove Car");
    removeCar.setActionCommand("Remove");
    main.add(removeCar);

    JMenuItem searchCars = new JMenuItem("Search Cars");
    searchCars.setActionCommand("Search");
    main.add(searchCars);

    setJMenuBar(mainMenu);

    /*
    //Add action listener for the Add Car button
    addCar.addActionListener
    (
        new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent e) 
                {
                    MainMenu.windowClosed();
                }    
            }        
    );
    */
}
}

AddForm.java:

package carparksystem;

import javax.swing.ButtonGroup;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import javax.swing.JFrame;

public class AddForm extends JFrame
{
public AddForm()
{        
    JLabel regNumLabel = new JLabel("Registration Number:");
    JLabel highValLabel = new JLabel("High Value?");
    JLabel largeLabel = new JLabel("Large Vehicle?");

    JRadioButton btnYesHighVal = new JRadioButton("Yes", false);
    JRadioButton btnNoHighVal = new JRadioButton("No", true);
    JRadioButton btnYesLarge = new JRadioButton("Yes", false);
    JRadioButton btnNoLarge = new JRadioButton("No", true);

    ButtonGroup highVal = new ButtonGroup();        //allows just one radio button from the group to be selected
    highVal.add(btnYesHighVal);
    highVal.add(btnNoHighVal);

    ButtonGroup largeCar = new ButtonGroup();       //allows just one radio button from the group to be selected
    largeCar.add(btnYesLarge);
    largeCar.add(btnNoLarge);

    JTextField regNumField = new JTextField();

    JButton addCar = new JButton("   Add  ");
    JButton addCancel = new JButton("Cancel");

    GroupLayout addLayout = new GroupLayout(getContentPane());      //chosen to display components in group layout
    getContentPane().setLayout(addLayout);
    addLayout.setAutoCreateGaps(true);
    addLayout.setAutoCreateContainerGaps(true);

    addLayout.setHorizontalGroup(addLayout.createSequentialGroup()
        .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(regNumLabel)
            .addComponent(highValLabel)
            .addComponent(largeLabel))
        .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(regNumField)
            .addGroup(addLayout.createSequentialGroup()
                .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
                .addComponent(btnYesHighVal)
                .addComponent(btnYesLarge))
            .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
                .addComponent(btnNoHighVal)
                .addComponent(btnNoLarge))))
        .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(addCar)
            .addComponent(addCancel))
    );

    addLayout.setVerticalGroup(addLayout.createSequentialGroup()
        .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
            .addComponent(regNumLabel)
            .addComponent(regNumField)
            .addComponent(addCar))
        .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(addLayout.createSequentialGroup()
                .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(highValLabel)
                    .addComponent(btnYesHighVal)
                    .addComponent(btnNoHighVal))
                .addGroup(addLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(largeLabel)    
                    .addComponent(btnYesLarge)
                    .addComponent(btnNoLarge)))
                .addComponent(addCancel))
        );

    setSize(375, 150);
    setTitle("Add Car");
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}

On the main frame I have drawn a series of shapes which will appear on the Frame at first. And there will be a JMenu above this with the JMenuItems Add, Remove and Search Cars in the menu.

Once these Add Remove and Search 'menu buttons' are clicked they will open the corresponding form which will allow the user to input data.

When I run my code with and without the action listeners, it runs as normal but the menus don't link at all. It's as if they have no meaning?

1

There are 1 answers

3
Hovercraft Full Of Eels On

Some basic issues:

  • If one class is to have an effect on another, it will need a reference to that other class so that it can all its methods.
  • Thus if your MainMenu class needs to change the state of the AddForm class, then one way for this to occur is for MainMenu will need an AddForm field, and more importantly, that field must reference the current active AddForm object.
  • An even better program structure is to use an M-V-C or Model-View-Control structure where the two views are connected by a shared model, but this may be a bit advanced for your simple program. Still if you want to follow best practices, this would be the way to go. This example shows a more simple View-Control idea that also could work.
  • It's usually a bad idea for a GUI to have multiple top-level windows, which for Java means multiple JFrames. Better to have one main window and other views that can be swapped in it if need be with a CardLayout, or that can be displayed in dialog windows if need be. Please check out The Use of Multiple JFrames, Good/Bad Practice?.

For a simple non-MVC example, you could have one class calling methods of the second. Assume a JPanel that holds a JList called View1 that has a public method, addItem(String item) that adds items tot he JList's model:

public class View1 extends JPanel {
   private static final String PROTOTYPE = String.format("%50s", " ");
   private DefaultListModel<String> listModel = new DefaultListModel<>();
   private JList<String> list = new JList<>(listModel);

   public View1() {
      list.setPrototypeCellValue(PROTOTYPE);
      list.setVisibleRowCount(8);
      JScrollPane scrollPane = new JScrollPane(list);
      scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
      add(scrollPane);
   }

   public void addItem(String item) {
      listModel.addElement(item);
   }
}

Consider a second JPanel class, View2, that holds a JTextArea and a JButton and an instance of View1. This second class could then call the addItem(...) method of View1:

public class View2 extends JPanel {
   private View1 view1;
   private JTextField textField = new JTextField(10);

   public View2(View1 view1) {
      Action addItemAction = new AddItemAction();
      this.view1 = view1;
      add(textField);
      add(new JButton(addItemAction));
      textField.setAction(addItemAction);
   }

   private class AddItemAction extends AbstractAction {
      public AddItemAction() {
         super("Add Item");
         putValue(MNEMONIC_KEY, KeyEvent.VK_A);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         view1.addItem(textField.getText()); // *** calls view1's method here
         textField.selectAll();
      }
   }
}

You then could create the two classes, passing View1 into View2's constructor

  View1 view1 = new View1();
  View2 view2 = new View2(view1);

And then put one into a JFrame the other into a non-modal JDialog and display. For instance:

import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class SimpleGuis {
   private static void createAndShowGui() {
      View1 view1 = new View1();
      View2 view2 = new View2(view1);

      JFrame frame = new JFrame("SimpleGuis");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(view1);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);

      JDialog dialog = new JDialog(frame, "View2", ModalityType.MODELESS);
      dialog.add(view2);
      dialog.pack();
      dialog.setLocationByPlatform(true);
      dialog.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class View1 extends JPanel {
   private static final String PROTOTYPE = String.format("%50s", " ");
   private DefaultListModel<String> listModel = new DefaultListModel<>();
   private JList<String> list = new JList<>(listModel);

   public View1() {
      list.setPrototypeCellValue(PROTOTYPE);
      list.setVisibleRowCount(8);
      JScrollPane scrollPane = new JScrollPane(list);
      scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
      add(scrollPane);
   }

   public void addItem(String item) {
      listModel.addElement(item);
   }
}

class View2 extends JPanel {
   private View1 view1;
   private JTextField textField = new JTextField(10);

   public View2(View1 view1) {
      Action addItemAction = new AddItemAction();
      this.view1 = view1;
      add(textField);
      add(new JButton(addItemAction));
      textField.setAction(addItemAction);
   }

   private class AddItemAction extends AbstractAction {
      public AddItemAction() {
         super("Add Item");
         putValue(MNEMONIC_KEY, KeyEvent.VK_A);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         view1.addItem(textField.getText());
         textField.selectAll();
      }
   }
}