【问题标题】:java.util.observer with multiple JFrame带有多个 JFrame 的 java.util.observer
【发布时间】:2015-11-08 12:56:24
【问题描述】:

我正在尝试使用多个 JFrame 实例来实现观察者模式。但是,当调用notifyObservers() 时,只会更新最后一个实例化的 JFrame 实例。

这是我的代码:

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;

    }                

这个想法是,当客户进入商店时,商店将为客户打开一个新的 JFrame。当管理员添加商店时,所有 JFrame CustomerFrame.java 的列表项都会更新。然而,在我的例子中,只有最后实例化的CustomerFrame.java 会得到更新,而其他的保持不变。

Opening 2 New Customer 可以重现上述问题,并尝试添加店铺AdminFrame.java

这里有什么问题?

【问题讨论】:

标签: java swing observer-pattern observers


【解决方案1】:

您不会从 MallCustomer 的 update(...) 方法中调用 thisFrame 上的任何方法。与其在 storeList 上设置模型(这到底是从哪里来的?),不如为 CustomerFrame 提供一个公共方法,比如在 update 中调用的名为 setStoreListModel(ListModel listModel) 的方法,并传入 storeModel。

即,

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);
    }
}

@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);
}    

注意:代码未经编译或测试


编辑

我可以看到您有几个问题:

  • 您的 JList 不应是公共的或静态的。将其设为私有实例字段。
  • 再次(正如我一直建议的那样),为 Customer 窗口提供一个公共 setListModel 类型的方法。
  • 一个应用程序应该只有一个主 JFrame,因此 CustomerFrame JFrame 不应该是一个 JFrame,而应该是一个非模态 JDialog,或者最好是一个可以放置在任何地方的 JPanel——在它自己的 JDialog 中,在主要的 JFrame。
  • 将主 JFrame 传递到您的 JDialogs 中,以便对话框可以在其超级构造函数中注册父 JFrame。
  • 将 CustomerDialog 实例传递给您的 MallObserver 实例,然后使用它在 MallObserver 内部设置一个字段。
  • 在update方法中,创建或更新模型并在MallObserver持有的客户对话实例上调用`setListModel。
  • 调用initComponents()之前创建 Mall 实例。这样,您就可以在动作侦听器方法中使用相同的 Mall 实例。
  • 挑剔:在创建和发布 MCVE 时,摆脱杂乱和分散注意力的 NetBeans 生成的代码。而是只发布您自己创建的简单代码和简单 GUI,类似于我在下面所做的更改。
  • 你的 MCVE 应该都放在一个文件中。该文件可以有多个类,但我们应该很容易将其剪切并粘贴到我们的 IDE 中然后运行。

例如:

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;
    }


}

【讨论】:

  • @RyanLiew:您不完整的问题已更改,现在使我的回答无效。以后,请提出完整的问题,包括发帖minimal reproducible example,以免我们浪费时间。请阅读minimal reproducible example 链接,以便创建、发布并回答您的问题。
  • 很抱歉...我已经用可以重现问题的代码编辑了问题。
  • @RyanLiew:看起来您误用了静态字段。您的 JList 绝对应该是静态的或公开的。
  • 抱歉回复晚了。一直在努力落实您的建议。根据您的答案修复代码后,它现在可以工作了。非常感谢!标记为正确答案
  • 已注意到您对未来发布 MCVE 的建议。非常感谢!
猜你喜欢
  • 2011-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 2021-09-01
  • 2012-08-20
相关资源
最近更新 更多