1

I have 2 classes. Both implements runnable to create the GUI. The first one is the main, and the second one is the secondary class.

I want within the actionlistener of the main class to startup the secondary class.

Here is the code (the two classes are separated files):

public class Main implements Runnable
{
    private JTextField txt1, txt2;
    private JLabel lbl1, lbl2;

    public void run() 
    {
        JFrame frame = new JFrame("Secondary");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container pane = frame.getContentPane();

        JPanel background = new JPanel();
        background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));

        ......... 

        // Horizontally adding the textbox and button in a Box
        Box box = new Box(BoxLayout.Y_AXIS);
        ......

        background.add(box);
        pane.add(background);

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

    private class SListener implements ActionListener 
    {
        public void actionPerformed(ActionEvent a)
        {
            Secondary s = new Secondary();
        }
    } 

    public static void main (String[] args) 
    {
        Main gui = new Main();
        SwingUtilities.invokeLater(gui);
    }

}


public class Secondary implements Runnable
{
    private JTextField txt1, txt2;
    private JLabel lbl1, lbl2;

    public Secondary()
    {
      Secondary gui = new Secondary();
      SwingUtilities.invokeLater(gui);
    }

    public void run() 
    {
        JFrame frame = new JFrame("Secondary");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container pane = frame.getContentPane();

        JPanel background = new JPanel();
        background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));

        ......... 

        // Horizontally adding the textbox and button in a Box
        Box box = new Box(BoxLayout.Y_AXIS);
        ......

        background.add(box);
        pane.add(background);

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

I want to keep the code in two files, I don't want to mixed the two classes in one file. As you can see from the code, in the Secondary class, in it's constructor I create an Instance of the Secondary class and I run the gui so that when the Instance of this class is created in the Main class, to run the gui.

Unfortunately this technique is not working.

Any ideas? Thanks

Phrixus
  • 1,209
  • 2
  • 19
  • 36
  • 1
    Don't use a second frame. Rather use a `JDialog` or the `CardLayout` for these purposes. See also [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/a/9554657/928711) – Guillaume Polet Jan 18 '15 at 15:22

2 Answers2

2

The following line are complety wrong:

public Secondary(){
  Secondary gui = new Secondary();
  SwingUtilities.invokeLater(gui);
}

Each time you call new Secondary() somewhere in your code, the above code will be triggered, which in turn calls new Secondary() again, and again, and again, ... and your program is blocked.

You probably want to replace it either by

public Secondary(){
      SwingUtilities.invokeLater(this);
}

which will avoid the loop, but this is weird behaviour for a constructor.

It makes much more sense to switch to an empty constructor (or delete it all together)

public Secondary(){
}

and rewrite your listener to

public void actionPerformed(ActionEvent a){
  Secondary s = new Secondary();
  SwingUtilities.invokeLater( s );
}
Robin
  • 36,233
  • 5
  • 47
  • 99
  • You were right! But a new problem arise: when I press the close button of the Secondary window, it causes that all the program is terminated. Can I do anything to avoid this behaviour ? – Phrixus Jan 18 '15 at 15:28
  • Remove the `frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);` line – Robin Jan 18 '15 at 15:32
  • In your re-written listener, why would you want to queue code onto the event queue **from within the event queue**? But 1+ for your catching the recursion! – Hovercraft Full Of Eels Jan 18 '15 at 15:39
  • @HovercraftFullOfEels good point. I just moved the code around and didn't pay any attention on the why of an `invokeLater` call on the EDT – Robin Jan 18 '15 at 15:47
2

I would recommend that you completely re-design your program. I find that it is most helpful to gear my GUI's towards creation of JPanels, not top level windows such as JFrame, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. I find that this greatly increase the flexibility of my GUI coding, and is exactly what I suggest that you do. So...

  • Your first class creates a JPanel that is then placed into a JFrame.
  • In the first class's ActionListener, create an instance of the 2nd class, place it into a JDialog (not a JFrame), and then display it.

For example,

import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class TwoWindowEg {
   public TwoWindowEg() {
      // TODO Auto-generated constructor stub
   }

   private static void createAndShowGui() {
      GuiPanel1 mainPanel = new GuiPanel1();

      JFrame frame = new JFrame("Main GUI");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

class GuiPanel1 extends JPanel {
   private static final int PREF_W = 800;
   private static final int PREF_H = 650;
   private GuiPanel2 guiPanel2 = new GuiPanel2(); // our second class!
   private JDialog dialog = null;  // our JDialog

   public GuiPanel1() {
      setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
      add(new JButton(new LaunchNewWindowAction("Launch New Window")));
      add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   private class LaunchNewWindowAction extends AbstractAction {
      public LaunchNewWindowAction(String name) {
         super(name);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         if (dialog == null) {
            // get the Window that holds this JPanel
            Window win = SwingUtilities.getWindowAncestor(GuiPanel1.this);
            dialog = new JDialog(win, "Second Window", ModalityType.APPLICATION_MODAL);
            dialog.add(guiPanel2);
            dialog.pack();
         }
         dialog.setVisible(true);
      }
   }
}

class GuiPanel2 extends JPanel {
   public GuiPanel2() {
      setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
      add(new JLabel("The second JPanel/Class"));
      add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
   }
}

class DisposeAction extends AbstractAction {
   public DisposeAction(String name, int mnemonic) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      Component comp = (Component) e.getSource();
      Window win = SwingUtilities.getWindowAncestor(comp);
      win.dispose();
   }
}

Alternatively, you could swap JPanel "views" using a CardLayout, but either way, you will want to avoid showing two JFrames. Please have a look at The Use of Multiple JFrames, Good/Bad Practice?.

Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373