Search code examples
javaswingmethodscardlayout

Passing data between classes in CardLayout


Please have a look at the following code

WizardPanel.java

package wizardGUI;

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

public class WizardPanel extends JDialog
{
    private JPanel cardPanel, buttonPanel;
    private JButton next,previous;
    private CardLayout c1;

    private FileSelector fileSelector;
    private DelemeterSelector delemeterSelector;

    private int count = 1;

    public WizardPanel()
    {
        //Intializing instance variables
        fileSelector = FileSelector.getInstance();
        delemeterSelector = DelemeterSelector.getInstance();

        cardPanel = new JPanel();
        c1 = new CardLayout();
        cardPanel.setLayout(c1);

        cardPanel.add(fileSelector,"1");
        cardPanel.add(delemeterSelector,"2");

        c1.show(cardPanel, "1");;


        buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));

        next = new JButton("Next");
        next.addActionListener(new NextButtonAction());
        previous = new JButton("Previous");

        buttonPanel.add(next);
        buttonPanel.add(previous);

        //Creating the GUI
        this.setLayout(new BorderLayout());
        this.add(cardPanel,"Center");
        this.add(buttonPanel,"South");

        this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        this.setResizable(true);
        this.pack();
        this.setVisible(true);

    }

    private class NextButtonAction implements ActionListener
    {
        public void actionPerformed(ActionEvent ae)
        {

                c1.show(cardPanel, "2");

        }
    }
}

FileSelector.java

package wizardGUI;

/*This is the first panel is wazard GUI. Using this window user can select the correct file
  which contains the data required to create the table
 */

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

public class FileSelector extends JPanel
{
    private JLabel fileName, description;
    private JTextField fileTxt;
    private JButton browse;

    private GridBagLayout gbl;
    private GridBagConstraints gbc;

    private static FileSelector instance = null;

    private FileSelector()
    {
        //Intializing instance variables
        fileName = new JLabel("File Name: ");
        description = new JLabel("Specify the source of the data");

        fileTxt = new JTextField(10);

        browse = new JButton("Browse");

        gbl = new GridBagLayout();
        gbc = new GridBagConstraints();

        //Creating GUI
        this.setLayout(gbl);

        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.weightx = 0.0;
        gbc.weighty = 0.0;
        gbc.fill = GridBagConstraints.BOTH;
        this.add(description,gbc);

        gbc.gridx = 1;
        gbc.gridy = 2;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(0,10,0,0);
        this.add(locationPanel(),gbc);

        this.setBorder(BorderFactory.createEmptyBorder());
    }

    private JPanel locationPanel()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());

        panel.add(fileName);
        panel.add(fileTxt);
        panel.add(browse);

        return panel;
    }

    public static FileSelector getInstance()
    {
        if(instance==null)
        {
            instance = new FileSelector();
        }

        return instance;
    }
}

DelemeterSelector.java

/*This is the second windows in wizard
This class is designed to let the user to select the delemeter to break information */

package wizardGUI;

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

public class DelemeterSelector extends JPanel
{
    private JLabel description;
    private JRadioButton tabBtn, semicolanBtn, commaBtn, spaceBtn;
    private JTextArea txtArea;
    private JScrollPane scroll;
    private ButtonGroup btnGroup;

    private GridBagLayout gbl;
    private GridBagConstraints gbc;

    private static DelemeterSelector instance = null;

    private DelemeterSelector()
    {
        //Initializing instance variables
        description = new JLabel("What delemeter separates your fields? Select the appropreiate delemeter");

        tabBtn = new JRadioButton("Tab");
        semicolanBtn = new JRadioButton("Semicolan");
        commaBtn = new JRadioButton("Comma");
        spaceBtn = new JRadioButton("Space");

        btnGroup = new ButtonGroup();
        btnGroup.add(tabBtn);
        btnGroup.add(semicolanBtn);
        btnGroup.add(commaBtn);
        btnGroup.add(spaceBtn);

        txtArea = new JTextArea(20,70);

        scroll = new JScrollPane(txtArea);

        gbl = new GridBagLayout();
        gbc = new GridBagConstraints();

        this.setLayout(gbl);

        //Creating the GUI
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(20,0,0,0);
        this.add(description,gbc);

        gbc.gridx = 1;
        gbc.gridy = 2;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(20,0,0,0);
        this.add(radioPanel(),gbc);

        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.insets = new Insets(10,0,0,0);
        gbc.fill = GridBagConstraints.BOTH;
        this.add(scroll,gbc);
    }

    private JPanel radioPanel()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());

        panel.add(tabBtn);
        panel.add(semicolanBtn);
        panel.add(commaBtn);
        panel.add(spaceBtn);

        panel.setBorder(BorderFactory.createTitledBorder("Choose the Delimeter that seperates your fields"));

        return panel;
    }

    public static DelemeterSelector getInstance()
    {
        if(instance == null)
        {
            instance = new DelemeterSelector();
        }

        return instance;
    }
}

Main.java

package wizardGUI

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

    public class Main extends JFrame
    {
        public Main()
        {
            new WizardPanel().setVisible(true);
        }

        public static void main(String[]args)
        {
            try
            {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                new Main();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

In here, we have only two buttons "Next" and "Previous" and they are commong for all the JPanels because it appears in "South" of 'WizardPanel' while other JPanels are in center.

Now, lets take "FileSelector". Guess I selected a file using the browse button.

Now how can I send this information to the next panel? which means to "DelemeterSelector" ?. I have only 2 'COMMON' buttons "Next" and "previous" which are located in ANOTHER CLASS. I don't think writing business logic (getting selected file from 'FileSelector' and sending it to 'DelemeterSelector') inside that "Next" or "Previous" button's actionListener is a good idea. Because, I have to access another class, get it data and send it to the next JPanel (next class). There will be more JPanels, so this will be really hard and failing for sure.

I thought of adding "Next" and "Previous" buttons to each and every JPanel rather than the common ones. But then the problem is moving from one panel to another, because the reference to CardLayout is missing.

Please help me to find a correct and efficient way pass data from one class to another class, in this wizard. Thanks.

PS:

I don't want to do condition checking in "Next" buttons action class and let it hold all the actions. For an example, see the following EXAMPLE CODE snippet

//NON TESTED, NON COMPILED EXAMPLE CODE.
private class NextButtonAction implements ActionListener
{
         public void actionPerformed(ActionEvent ae)
         {

              if(fileSelector.isVisible(true))
              {
                  //Access FileSelector
                  // Get the Data
                  // send data into delemeter class
               }
               else if(delemeterSelector.isVisible(true))
               {
                  //Access delemeterSelector
                  // Get the Data
                  // send data into other class
               }
                else if(SOME_OTHER_CLASS.isVisible(true))
               {
                  //Access delemeterSelector
                  // Get the Data
                  // send data into other class
               }
       }
}

Solution

  • Lunch break is over, so all I have time for is to post sample code, but I'll try to come back to explain it before the end of the day:

    import java.awt.*;
    import java.awt.event.*;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    
    import javax.swing.*;
    import javax.swing.event.SwingPropertyChangeSupport;
    
    public class MainGui {
       public MainGui() {
          new WizardPanel().setVisible(true);
       }
    
       public static void main(String[] args) {
          try {
             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
             new MainGui();
          } catch (Exception e) {
             e.printStackTrace();
          }
       }
    }
    
    class WizardPanel extends JDialog {
       private JPanel cardPanel, buttonPanel;
       private JButton next, previous;
       private CardLayout c1;
       private SimpleModel simpleModel = new SimpleModel();
    
       private FileSelector fileSelector;
       private DelemeterSelector delemeterSelector;
    
       private int count = 1;
    
       public WizardPanel() {
          fileSelector = FileSelector.getInstance();
          delemeterSelector = DelemeterSelector.getInstance();
    
          fileSelector.setModel(simpleModel); //!!
          delemeterSelector.setModel(simpleModel); //!!
    
          cardPanel = new JPanel();
          c1 = new CardLayout();
          cardPanel.setLayout(c1);
    
          cardPanel.add(fileSelector, "1");
          cardPanel.add(delemeterSelector, "2");
    
          c1.show(cardPanel, "1");
    
          buttonPanel = new JPanel();
          buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
    
          next = new JButton("Next");
          next.addActionListener(new NextButtonAction());
          previous = new JButton("Previous");
    
          buttonPanel.add(next);
          buttonPanel.add(previous);
    
          // Creating the GUI
          this.setLayout(new BorderLayout());
          this.add(cardPanel, "Center");
          this.add(buttonPanel, "South");
    
          this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
          this.setResizable(true);
          this.pack();
          this.setVisible(true);
    
       }
    
       private class NextButtonAction implements ActionListener {
          public void actionPerformed(ActionEvent ae) {
    
             // c1.show(cardPanel, "2");
             c1.next(cardPanel); //!!
    
          }
       }
    }
    
    class FileSelector extends JPanel {
       private JLabel fileName, description;
       private JTextField fileTxt;
       private JButton browse;
    
       private GridBagLayout gbl;
       private GridBagConstraints gbc;
       private SimpleModel simpleModel;
    
       private static FileSelector instance = null;
    
       private FileSelector() {
          // Intializing instance variables
          fileName = new JLabel("File Name: ");
          description = new JLabel("Specify the source of the data");
    
          fileTxt = new JTextField(10);
    
          browse = new JButton("Browse");
          browse.addActionListener(new ActionListener() {
    
             @Override
             public void actionPerformed(ActionEvent e) {
                if (simpleModel != null) {
                   simpleModel.setFileText(fileTxt.getText());
                }
             }
          });
    
          gbl = new GridBagLayout();
          gbc = new GridBagConstraints();
    
          // Creating GUI
          this.setLayout(gbl);
    
          gbc.gridx = 1;
          gbc.gridy = 1;
          gbc.weightx = 0.0;
          gbc.weighty = 0.0;
          gbc.fill = GridBagConstraints.BOTH;
          this.add(description, gbc);
    
          gbc.gridx = 1;
          gbc.gridy = 2;
          gbc.weightx = 1.0;
          gbc.fill = GridBagConstraints.BOTH;
          gbc.insets = new Insets(0, 10, 0, 0);
          this.add(locationPanel(), gbc);
    
          this.setBorder(BorderFactory.createEmptyBorder());
       }
    
       public void setModel(SimpleModel simpleModel) {
          this.simpleModel = simpleModel;
       }
    
       private JPanel locationPanel() {
          JPanel panel = new JPanel();
          panel.setLayout(new FlowLayout());
    
          panel.add(fileName);
          panel.add(fileTxt);
          panel.add(browse);
    
          return panel;
       }
    
       public static FileSelector getInstance() {
          if (instance == null) {
             instance = new FileSelector();
          }
    
          return instance;
       }
    }
    
    class DelemeterSelector extends JPanel {
       private JLabel description;
       private JRadioButton tabBtn, semicolanBtn, commaBtn, spaceBtn;
       private JTextArea txtArea;
       private JScrollPane scroll;
       private ButtonGroup btnGroup;
    
       private GridBagLayout gbl;
       private GridBagConstraints gbc;
       private SimpleModel simpleModel;
    
       private static DelemeterSelector instance = null;
    
       private DelemeterSelector() {
          description = new JLabel(
                "What delemeter separates your fields? Select the appropreiate delemeter");
    
          tabBtn = new JRadioButton("Tab");
          semicolanBtn = new JRadioButton("Semicolan");
          commaBtn = new JRadioButton("Comma");
          spaceBtn = new JRadioButton("Space");
    
          btnGroup = new ButtonGroup();
          btnGroup.add(tabBtn);
          btnGroup.add(semicolanBtn);
          btnGroup.add(commaBtn);
          btnGroup.add(spaceBtn);
    
          txtArea = new JTextArea(20, 70);
    
          scroll = new JScrollPane(txtArea);
    
          gbl = new GridBagLayout();
          gbc = new GridBagConstraints();
    
          this.setLayout(gbl);
    
          // Creating the GUI
          gbc.gridx = 1;
          gbc.gridy = 1;
          gbc.fill = GridBagConstraints.BOTH;
          gbc.insets = new Insets(20, 0, 0, 0);
          this.add(description, gbc);
    
          gbc.gridx = 1;
          gbc.gridy = 2;
          gbc.fill = GridBagConstraints.BOTH;
          gbc.insets = new Insets(20, 0, 0, 0);
          this.add(radioPanel(), gbc);
    
          gbc.gridx = 1;
          gbc.gridy = 3;
          gbc.insets = new Insets(10, 0, 0, 0);
          gbc.fill = GridBagConstraints.BOTH;
          this.add(scroll, gbc);
       }
    
       private JPanel radioPanel() {
          JPanel panel = new JPanel();
          panel.setLayout(new FlowLayout());
    
          panel.add(tabBtn);
          panel.add(semicolanBtn);
          panel.add(commaBtn);
          panel.add(spaceBtn);
    
          panel.setBorder(BorderFactory
                .createTitledBorder("Choose the Delimeter that seperates your fields"));
    
          return panel;
       }
    
       //!!
       public void setModel(final SimpleModel simpleModel) {
          this.simpleModel = simpleModel;
          simpleModel.addPropertyChangeListener(new PropertyChangeListener() {
    
             @Override
             public void propertyChange(PropertyChangeEvent evt) {
                if (SimpleModel.FILE_TEXT.equals(evt.getPropertyName())) {
                   txtArea.append("File Text: " + simpleModel.getFileText() + "\n");
                }         
             }
          });
       }
    
       public static DelemeterSelector getInstance() {
          if (instance == null) {
             instance = new DelemeterSelector();
          }
    
          return instance;
       }
    }
    
    class SimpleModel {
       public static final String FILE_TEXT = "file text";
       private SwingPropertyChangeSupport pcSupport = 
          new SwingPropertyChangeSupport(this);
       private String fileText;
    
       public void addPropertyChangeListener(PropertyChangeListener listener) {
          pcSupport.addPropertyChangeListener(listener);
       }
    
       public void removePropertyChangeListener(PropertyChangeListener listener) {
          pcSupport.removePropertyChangeListener(listener);
       }
    
       public void setFileText(String fileText) {
          String oldValue = this.fileText;
          String newValue = fileText;
          this.fileText = fileText;
          pcSupport.firePropertyChange(FILE_TEXT, oldValue , newValue);
    
       }
    
       public String getFileText() {
          return fileText;
       }
    
    }
    

    Edit explanation of code:

    This is a gross simplification of the Model-View-Controller or MVC design pattern since it is nothing more than model and view. The model holds the data and logic that the GUI works with and is essentially the "brains" of the program while the GUI is little more than a dumb display of the information.

    The model here is quite simple and only holds one String field called fileText. It has setters and getters for this field, and the most interesting thing about it is the setter is wired so that it will notify any listeners that want to know if this field is ever changed. This process of using PropertyChangeSupport and allowing for PropertyChangeListeners means the the fileText field is a "bound" property.

    Both of your JPanels have fields to hold a reference to the same SimpleModel object, and this model is set via a setter method, setModel(...).

      fileSelector.setModel(simpleModel); // !!
      delemeterSelector.setModel(simpleModel); // !!
    

    I let the FileSelector object set the fileText field if its JButton is pressed:

      browse.addActionListener(new ActionListener() {
    
         @Override
         public void actionPerformed(ActionEvent e) {
            if (simpleModel != null) {
               simpleModel.setFileText(fileTxt.getText());
            }
         }
      });
    

    I have the DelemeterSelector class add a PropertyChangeListener to the model so that it is notified whenever the fileText field's value is changed, and can respond to it as it sees fit:

    public void setModel(final SimpleModel simpleModel) {
      this.simpleModel = simpleModel;
      simpleModel.addPropertyChangeListener(new PropertyChangeListener() {
    
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if (SimpleModel.FILE_TEXT.equals(evt.getPropertyName())) {
               txtArea.append("File Text: " + simpleModel.getFileText() + "\n");
            }
         }
      });
    }
    

    Note that the model class has no idea what the GUI does with the information it holds, and that's a good thing as it means that "coupling" is fairly loose.