Ryan Liew
Ryan Liew

Reputation: 33

java.util.observer with multiple JFrame

I am trying to implement Observer pattern with multiple JFrame instances. However, when notifyObservers() is called, only the last instantiated JFrame instance is updated.

This is my code:

Mall.java

    import java.util.ArrayList;
    import java.util.Observable;

    public class Mall extends Observable{
        private ArrayList<String> stores;

        public Mall(){
           stores = new ArrayList<String>();
        }

        public void addNewStore(String store){
           stores.add(store);
           System.out.println(store);
           setChanged();
           notifyObservers(store);
        }

        public ArrayList<String> getStores(){
            return stores;
        }
    }

CustomerFrame.java

    public class CustomerFrame extends javax.swing.JFrame {

        public CustomerFrame() {
           initComponents();
        }

        public CustomerFrame(Mall theMall) {
            initComponents();
            MallObserver mallObs = new MallObserver();
            theMall.addObserver(mallObs);

        }

        private void initComponents() {

            jScrollPane1 = new javax.swing.JScrollPane();
            jList1 = new javax.swing.JList();

            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

            jList1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
            jScrollPane1.setViewportView(jList1);

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 249, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(141, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(124, Short.MAX_VALUE))
            );

            pack();
        }                   

        public static javax.swing.JList jList1;
        private javax.swing.JScrollPane jScrollPane1;                  
    }

MallObserver.java

    import java.util.Observable;
    import java.util.Observer;
    import javax.swing.DefaultListModel;

    public class MallObserver implements Observer{

        private Mall mallUpdate;

        @Override
        public void update(Observable o, Object arg) {
           mallUpdate = (Mall) o;
           DefaultListModel listModel = new DefaultListModel();
           for(int i = 0; i < mallUpdate.getStores().size(); i++)
               listModel.addElement(mallUpdate.getStores().get(i));
           CustomerFrame.jList1.setModel(listModel);
        }

    }

AdminFrame.java

        public class AdminFrame extends javax.swing.JFrame {

        Mall theMall;

        public AdminFrame() {
            initComponents();
            theMall = new Mall();
        }
    @SuppressWarnings("unchecked")                         
        private void initComponents() {

            jTextField1 = new javax.swing.JTextField();
            jLabel1 = new javax.swing.JLabel();
            jButton1 = new javax.swing.JButton();
            jButton2 = new javax.swing.JButton();

            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

            jTextField1.setText("jTextField1");

            jLabel1.setText("jLabel1");

            jButton1.setText("Add Store");
            jButton1.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jButton1ActionPerformed(evt);
                }
            });

            jButton2.setText("New Customer");
            jButton2.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jButton2ActionPerformed(evt);
                }
            });

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addContainerGap(143, Short.MAX_VALUE)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jButton2)
                .addComponent(jButton1)
                .addGroup(layout.createSequentialGroup()
                    .addComponent(jLabel1)
                    .addGap(26, 26, 26)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
            .addGap(138, 138, 138))
            );
            layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(129, 129, 129)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addComponent(jLabel1))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jButton1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addComponent(jButton2)
            .addContainerGap(88, Short.MAX_VALUE))
            );

            pack();
        }

     private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {                                         
            CustomerFrame newCust = new CustomerFrame();
            newCust.setVisible(true);
        } 

     private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
           theMall.addNewStore(jTextField1.getText());
        }

     public static void main(String args[]) {

            try {
                for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (ClassNotFoundException ex) {
                java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (InstantiationException ex) {
                java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            } catch (javax.swing.UnsupportedLookAndFeelException ex) {
                        java.util.logging.Logger.getLogger(AdminFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
                    }

     java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new AdminFrame().setVisible(true);
                }
            });
        }


        private javax.swing.JButton jButton1;
        private javax.swing.JButton jButton2;
        private javax.swing.JLabel jLabel1;
        private javax.swing.JTextField jTextField1;

    }                

The idea is, when a customer enters the store, the store will open up a new JFrame for the customer. When admin adds a store, all the JFrame CustomerFrame.java will have their list item updated. However, in my case, only the CustomerFrame.java that is last instantiated will get the update while the others remains the same.

The problem above can be reproduced by Opening 2 New Customer and try to Add Store at AdminFrame.java

What is the problem here?

Upvotes: 1

Views: 923

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

You don't call any methods on thisFrame from within the MallCustomer's update(...) method. Rather than setting the model on storeList (where the heck does that come from?), give CustomerFrame a public method, say called setStoreListModel(ListModel listModel) that you call in update, passing in the storeModel.

i.e.,

public class CustomerFrame extends javax.swing.JFrame {
    privarte Mall theMall;
    private JList storesList = new JList();

    public CustomerFrame(Customer customer, Mall theMall){
        MallCustomer mallObserver = new MallCustomer(this);
        theMall.addObserver(mallObserver);
    }

    public setStoresListModel(ListModel listModel) {
        storesList.setModel(listModel);
    }
}

and

@Override
public void update(Observable o, Object arg) {
    mallUpdate = (Mall) o;
    DefaultListModel storeModel = new DefaultListModel();

    //Stores update
    for(int i = 0; i < mallUpdate.getStores().size();i++) {
        storeModel.addElement(mallUpdate.getStores().get(i));
    }
    // storesList.setModel(storeModel);//a JList variable
    thisFrame.setStoresListModel(storeModel);
}    

Note: code not compiled nor tested


Edit

You've got several issues that I can see:

  • Your JList should not be public nor static. Make it a private instance field.
  • Again (as I suggested all along), give the Customer window a public setListModel type of method.
  • An application should only have one main JFrame, and so the CustomerFrame JFrame should not be a JFrame but rather should be a non-modal JDialog, or maybe better a JPanel that can be placed anywhere -- in its own JDialog, in the main JFrame.
  • Pass the main JFrame into your JDialogs so that the dialog can register the parent JFrame in its super's constructor.
  • Pass the CustomerDialog instance into your MallObserver instance, and then use it to set a field inside of MallObserver.
  • In the update method, create or update the model and call `setListModel on the customer dialog instance that MallObserver holds.
  • Create Mall instance before calling initComponents(). This way you can use the same Mall instance inside of your action listener methods.
  • A nit-pick: when creating and posting MCVE's, get rid of the messy and distracting NetBeans generated code. Instead only post simple code and simple GUI's that you've created yourself, similar to the changes that I've made below.
  • And you MCVE should all fit in a single file. The file can have several classes, but it should be easy for us to cut and paste into our IDE's and then run.

For example:

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.awt.event.*;
import javax.swing.*;

@SuppressWarnings("serial")
public class AdminFrame extends javax.swing.JFrame {

    private Mall theMall = new Mall(); //!!

    public AdminFrame() {
        initComponents();
        //!! theMall = new Mall();
    }

    private void initComponents() {

        jTextField1 = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        addStoreBtn = new javax.swing.JButton();
        newCustBtn = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTextField1.setText("jTextField1");

        jLabel1.setText("jLabel1");

        addStoreBtn.setText("Add Store");
        addStoreBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                theMall.addNewStore(jTextField1.getText());
            }
        });
        addStoreBtn.setMnemonic(KeyEvent.VK_S);

        newCustBtn.setText("New Customer");
        newCustBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                // !! CustomerFrame newCust = new CustomerFrame();
                CustomerDialog newCust = new CustomerDialog(AdminFrame.this, theMall);
                newCust.pack();
                newCust.setLocationByPlatform(true);
                newCust.setVisible(true);
            }
        });
        newCustBtn.setMnemonic(KeyEvent.VK_C);

        JPanel mainPanel = new JPanel();
        mainPanel.add(jLabel1);
        mainPanel.add(jTextField1);
        mainPanel.add(addStoreBtn);
        mainPanel.add(newCustBtn);

        add(mainPanel);

        pack();
        setLocationRelativeTo(null);
    }

    public static void main(String args[]) {

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new AdminFrame().setVisible(true);
            }
        });
    }

    private javax.swing.JButton addStoreBtn;
    private javax.swing.JButton newCustBtn;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JTextField jTextField1;

}

class MallObserver implements Observer {

    private Mall mallUpdate;
    private CustomerDialog customerDialog; // !!

    // !!
    public MallObserver(CustomerDialog customerFrame) {
        this.customerDialog = customerFrame; // !!
    }

    @Override
    public void update(Observable o, Object arg) {
        mallUpdate = (Mall) o;
        DefaultListModel<String> listModel = new DefaultListModel<>();
        for (int i = 0; i < mallUpdate.getStores().size(); i++) {
            listModel.addElement(mallUpdate.getStores().get(i));
        }

        customerDialog.setListModel(listModel);
    }

}

@SuppressWarnings("serial")
class CustomerDialog extends JDialog {  //!!

    // !!!!!!! public CustomerFrame() {
    // initComponents();
    // }

    public void setListModel(ListModel<String> listModel) {
        jList1.setModel(listModel);
    }

    public CustomerDialog(AdminFrame adminFrame, Mall theMall) {
        super(adminFrame, "Customer Dialog", ModalityType.MODELESS);
        initComponents();
        // !! MallObserver mallObs = new MallObserver();
        MallObserver mallObs = new MallObserver(this); // !!
        theMall.addObserver(mallObs);
    }

    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new JList<>();
        jList1.setPrototypeCellValue("                               ");
        jList1.setVisibleRowCount(15);

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        jList1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane1.setViewportView(jList1);

        add(jScrollPane1);

        pack();
    }

    // public static javax.swing.JList jList1;
    private JList<String> jList1;
    private JScrollPane jScrollPane1;
}

class Mall extends Observable {
    private ArrayList<String> stores;

    public Mall() {
        stores = new ArrayList<String>();
    }

    public void addNewStore(String store) {
        stores.add(store);
        setChanged();
        notifyObservers(store);
    }

    public ArrayList<String> getStores() {
        return stores;
    }


}

Upvotes: 2

Related Questions