当前位置: 首页 > 知识库问答 >
问题:

Java和GUI--根据MVC模式,ActionListeners属于哪里?

经嘉
2023-03-14

我目前正在编写一个模板Java应用程序,不知何故,如果我想清晰地遵循MVC模式,我不确定ActionListeners属于哪里。

这个示例是基于Swing的,但它不是关于框架的,而是关于Java中MVC的基本概念,使用任何框架来创建GUI。

    null
package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import view.MainView;

public class MainController
{
    MainView mainView = new MainView();

    public MainController()
    {
        this.initViewActionListeners();
    }

    private void initViewActionListeners()
    {
        mainView.initButtons(new CloseListener());
    }

    public class CloseListener implements ActionListener
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            mainView.dispose();
        }
    }
}


查看:

package view;

import java.awt.Dimension;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainView extends JFrame
{
    JButton button_close    = new JButton();
    JPanel  panel_mainPanel = new JPanel();

    private static final long   serialVersionUID    = 5791734712409634055L;

    public MainView()
    {
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        this.setSize(500, 500);
        this.add(panel_mainPanel);
        setVisible(true);
    }

    public void initButtons(ActionListener actionListener)
    {
        this.button_close = new JButton("Close");
        this.button_close.setSize(new Dimension(100, 20));
        this.button_close.addActionListener(actionListener);
        this.panel_mainPanel.add(button_close);
    }
}

共有1个答案

柳韬
2023-03-14

这是一个很难用Swing回答的问题,因为Swing不是一个纯粹的MVC实现,视图和控制器是混合的。

从技术上讲,模型和控制器应该能够交互,控制器和视图应该能够交互,但是视图和模型不应该交互,这显然不是Swing的工作方式,但这是另一个争论...

另一个问题是,您真的不想向任何人公开UI组件,控制器不应该关心某些操作是如何发生的,只应该关心它们可以发生的。

public interface MainViewListener {
    public void didPerformClose(MainView mainView);
}

实际上有两个视图(不考虑实际的对话框),有凭据视图和登录视图,是的,正如您将看到的,它们是不同的。

凭据视图负责收集要进行身份验证的详细信息、用户名和密码。它将向控制器提供信息,让它知道这些凭据何时被更改,因为控制器可能想要采取一些操作,比如启用“登录”按钮...

视图还想知道什么时候要进行身份验证,因为它想要禁用它的字段,这样用户就不能在身份验证进行时更新视图,同样地,它也需要知道身份验证什么时候失败或成功,因为它需要对这些事件采取行动。

public interface CredentialsView {

    public String getUserName();
    public char[] getPassword();

    public void willAuthenticate();
    public void authenticationFailed();
    public void authenticationSucceeded();

    public void setCredentialsViewController(CredentialsViewController listener);

}

public interface CredentialsViewController {

    public void credientialsDidChange(CredentialsView view);

}
public class CredentialsPane extends JPanel implements CredentialsView {

    private CredentialsViewController controller;

    private JTextField userNameField;
    private JPasswordField passwordField;

    public CredentialsPane(CredentialsViewController controller) {
        setCredentialsViewController(controller);
        setLayout(new GridBagLayout());
        userNameField = new JTextField(20);
        passwordField = new JPasswordField(20);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = new Insets(2, 2, 2, 2);
        gbc.anchor = GridBagConstraints.EAST;
        add(new JLabel("Username: "), gbc);

        gbc.gridy++;
        add(new JLabel("Password: "), gbc);

        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(userNameField, gbc);
        gbc.gridy++;
        add(passwordField, gbc);

        DocumentListener listener = new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
            }
        };

        userNameField.getDocument().addDocumentListener(listener);
        passwordField.getDocument().addDocumentListener(listener);

    }

    @Override
    public CredentialsViewController getCredentialsViewController() {
        return controller;
    }

    @Override
    public String getUserName() {
        return userNameField.getText();
    }

    @Override
    public char[] getPassword() {
        return passwordField.getPassword();
    }

    @Override
    public void willAuthenticate() {
        userNameField.setEnabled(false);
        passwordField.setEnabled(false);
    }

    @Override
    public void authenticationFailed() {
        userNameField.setEnabled(true);
        passwordField.setEnabled(true);

        userNameField.requestFocusInWindow();
        userNameField.selectAll();

        JOptionPane.showMessageDialog(this, "Authentication has failed", "Error", JOptionPane.ERROR_MESSAGE);
    }

    @Override
    public void authenticationSucceeded() {
        // Really don't care, but you might want to stop animation, for example...
    }

    public void setCredentialsViewController(CredentialsViewController controller){
        this.controller = controller;
    }

}
public interface LoginView {

    public CredentialsView getCredentialsView();

    public void willAuthenticate();
    public void authenticationFailed();
    public void authenticationSucceeded();

    public void dismissView();

    public LoginViewController getLoginViewController();

}

public interface LoginViewController {

    public void authenticationWasRequested(LoginView view);
    public void loginWasCancelled(LoginView view);

}

因为LoginPane将需要根据CredentialsView中的更改来更改它自己的状态,所以在这种情况下允许LoginPane充当控制器是有意义的,否则,您需要提供更多的方法来控制按钮的状态,但这会将UI逻辑转移到控制器...

public static class LoginPane extends JPanel implements LoginView, CredentialsViewController {

    private LoginViewController controller;
    private CredentialsPane credientialsView;

    private JButton btnAuthenticate;
    private JButton btnCancel;

    private boolean wasAuthenticated;

    public LoginPane(LoginViewController controller) {
        setLoginViewController(controller);
        setLayout(new BorderLayout());
        setBorder(new EmptyBorder(8, 8, 8, 8));

        btnAuthenticate = new JButton("Login");
        btnCancel = new JButton("Cancel");

        JPanel buttons = new JPanel();
        buttons.add(btnAuthenticate);
        buttons.add(btnCancel);

        add(buttons, BorderLayout.SOUTH);

        credientialsView = new CredentialsPane(this);
        add(credientialsView);

        btnAuthenticate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getLoginViewController().authenticationWasRequested(LoginPane.this);
            }
        });
        btnCancel.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getLoginViewController().loginWasCancelled(LoginPane.this);
                // I did think about calling dispose here,
                // but's not really the the job of the cancel button to decide what should happen here...
            }
        });

        validateCreientials();

    }

    public static boolean showLoginDialog(LoginViewController controller) {

        final LoginPane pane = new LoginPane(controller);

        JDialog dialog = new JDialog();
        dialog.setTitle("Login");
        dialog.setModal(true);
        dialog.add(pane);
        dialog.pack();
        dialog.setLocationRelativeTo(null);
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        dialog.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                pane.getLoginViewController().loginWasCancelled(pane);
            }
        });
        dialog.setVisible(true);

        return pane.wasAuthenticated();

    }

    public boolean wasAuthenticated() {
        return wasAuthenticated;
    }

    public void validateCreientials() {

        CredentialsView view = getCredentialsView();
        String userName = view.getUserName();
        char[] password = view.getPassword();
        if ((userName != null && userName.trim().length() > 0) && (password != null && password.length > 0)) {

            btnAuthenticate.setEnabled(true);

        } else {

            btnAuthenticate.setEnabled(false);

        }

    }

    @Override
    public void dismissView() {
        SwingUtilities.windowForComponent(this).dispose();
    }

    @Override
    public CredentialsView getCredentialsView() {
        return credientialsView;
    }

    @Override
    public void willAuthenticate() {
        getCredentialsView().willAuthenticate();
        btnAuthenticate.setEnabled(false);
    }

    @Override
    public void authenticationFailed() {
        getCredentialsView().authenticationFailed();
        validateCreientials();
        wasAuthenticated = false;
    }

    @Override
    public void authenticationSucceeded() {
        getCredentialsView().authenticationSucceeded();
        validateCreientials();
        wasAuthenticated = true;
    }

    public LoginViewController getLoginViewController() {
        return controller;
    }

    public void setLoginViewController(LoginViewController controller) {
        this.controller = controller;
    }

    @Override
    public void credientialsDidChange(CredentialsView view) {
        validateCreientials();
    }

}
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import sun.net.www.protocol.http.HttpURLConnection;

public class Test {

    protected static final Random AUTHENTICATION_ORACLE = new Random();

    public static void main(String[] args) {
        new Test();
    }

    public interface CredentialsView {
        public String getUserName();
        public char[] getPassword();
        public void willAuthenticate();
        public void authenticationFailed();
        public void authenticationSucceeded();
        public CredentialsViewController getCredentialsViewController();
    }

    public interface CredentialsViewController {
        public void credientialsDidChange(CredentialsView view);
    }

    public interface LoginView {
        public CredentialsView getCredentialsView();
        public void willAuthenticate();
        public void authenticationFailed();
        public void authenticationSucceeded();
        public void dismissView();
        public LoginViewController getLoginViewController();
    }

    public interface LoginViewController {
        public void authenticationWasRequested(LoginView view);
        public void loginWasCancelled(LoginView view);
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                LoginViewController controller = new LoginViewController() {

                    @Override
                    public void authenticationWasRequested(LoginView view) {
                        view.willAuthenticate();
                        LoginAuthenticator authenticator = new LoginAuthenticator(view);
                        authenticator.authenticate();
                    }

                    @Override
                    public void loginWasCancelled(LoginView view) {

                        view.dismissView();

                    }
                };

                if (LoginPane.showLoginDialog(controller)) {

                    System.out.println("You shell pass");

                } else {

                    System.out.println("You shell not pass");

                }

                System.exit(0);

            }
        });
    }

    public class LoginAuthenticator {

        private LoginView view;

        public LoginAuthenticator(LoginView view) {
            this.view = view;
        }

        public void authenticate() {

            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            if (AUTHENTICATION_ORACLE.nextBoolean()) {
                                view.authenticationSucceeded();
                                view.dismissView();
                            } else {
                                view.authenticationFailed();
                            }
                        }
                    });
                }
            });
            t.start();

        }

    }

    public static class LoginPane extends JPanel implements LoginView, CredentialsViewController {

        private LoginViewController controller;
        private CredentialsPane credientialsView;

        private JButton btnAuthenticate;
        private JButton btnCancel;

        private boolean wasAuthenticated;

        public LoginPane(LoginViewController controller) {
            setLoginViewController(controller);
            setLayout(new BorderLayout());
            setBorder(new EmptyBorder(8, 8, 8, 8));

            btnAuthenticate = new JButton("Login");
            btnCancel = new JButton("Cancel");

            JPanel buttons = new JPanel();
            buttons.add(btnAuthenticate);
            buttons.add(btnCancel);

            add(buttons, BorderLayout.SOUTH);

            credientialsView = new CredentialsPane(this);
            add(credientialsView);

            btnAuthenticate.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    getLoginViewController().authenticationWasRequested(LoginPane.this);
                }
            });
            btnCancel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    getLoginViewController().loginWasCancelled(LoginPane.this);
                    // I did think about calling dispose here,
                    // but's not really the the job of the cancel button to decide what should happen here...
                }
            });

            validateCreientials();

        }

        public static boolean showLoginDialog(LoginViewController controller) {

            final LoginPane pane = new LoginPane(controller);

            JDialog dialog = new JDialog();
            dialog.setTitle("Login");
            dialog.setModal(true);
            dialog.add(pane);
            dialog.pack();
            dialog.setLocationRelativeTo(null);
            dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
            dialog.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    pane.getLoginViewController().loginWasCancelled(pane);
                }
            });
            dialog.setVisible(true);

            return pane.wasAuthenticated();

        }

        public boolean wasAuthenticated() {
            return wasAuthenticated;
        }

        public void validateCreientials() {

            CredentialsView view = getCredentialsView();
            String userName = view.getUserName();
            char[] password = view.getPassword();
            if ((userName != null && userName.trim().length() > 0) && (password != null && password.length > 0)) {

                btnAuthenticate.setEnabled(true);

            } else {

                btnAuthenticate.setEnabled(false);

            }

        }

        @Override
        public void dismissView() {
            SwingUtilities.windowForComponent(this).dispose();
        }

        @Override
        public CredentialsView getCredentialsView() {
            return credientialsView;
        }

        @Override
        public void willAuthenticate() {
            getCredentialsView().willAuthenticate();
            btnAuthenticate.setEnabled(false);
        }

        @Override
        public void authenticationFailed() {
            getCredentialsView().authenticationFailed();
            validateCreientials();
            wasAuthenticated = false;
        }

        @Override
        public void authenticationSucceeded() {
            getCredentialsView().authenticationSucceeded();
            validateCreientials();
            wasAuthenticated = true;
        }

        public LoginViewController getLoginViewController() {
            return controller;
        }

        public void setLoginViewController(LoginViewController controller) {
            this.controller = controller;
        }

        @Override
        public void credientialsDidChange(CredentialsView view) {
            validateCreientials();
        }

    }

    public static class CredentialsPane extends JPanel implements CredentialsView {

        private CredentialsViewController controller;

        private JTextField userNameField;
        private JPasswordField passwordField;

        public CredentialsPane(CredentialsViewController controller) {
            setCredentialsViewController(controller);
            setLayout(new GridBagLayout());
            userNameField = new JTextField(20);
            passwordField = new JPasswordField(20);

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.anchor = GridBagConstraints.EAST;
            add(new JLabel("Username: "), gbc);

            gbc.gridy++;
            add(new JLabel("Password: "), gbc);

            gbc.gridx = 1;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add(userNameField, gbc);
            gbc.gridy++;
            add(passwordField, gbc);

            DocumentListener listener = new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent e) {
                    getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
                }

                @Override
                public void removeUpdate(DocumentEvent e) {
                    getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    getCredentialsViewController().credientialsDidChange(CredentialsPane.this);
                }
            };

            userNameField.getDocument().addDocumentListener(listener);
            passwordField.getDocument().addDocumentListener(listener);

        }

        @Override
        public CredentialsViewController getCredentialsViewController() {
            return controller;
        }

        @Override
        public String getUserName() {
            return userNameField.getText();
        }

        @Override
        public char[] getPassword() {
            return passwordField.getPassword();
        }

        @Override
        public void willAuthenticate() {
            userNameField.setEnabled(false);
            passwordField.setEnabled(false);
        }

        @Override
        public void authenticationFailed() {
            userNameField.setEnabled(true);
            passwordField.setEnabled(true);

            userNameField.requestFocusInWindow();
            userNameField.selectAll();

            JOptionPane.showMessageDialog(this, "Authentication has failed", "Error", JOptionPane.ERROR_MESSAGE);
        }

        @Override
        public void authenticationSucceeded() {
            // Really don't care, but you might want to stop animation, for example...
        }

        public void setCredentialsViewController(CredentialsViewController controller) {
            this.controller = controller;
        }

    }

}
 类似资料:
  • 问题内容: 我目前正在编写模板Java应用程序,并且如果我想完全遵循MVC模式,就不确定ActionListener的位置。 该示例基于Swing,但与框架无关,而是Java中使用任何框架创建GUI的MVC的基本概念。 我从一个绝对简单的应用程序开始,该应用程序包含一个JFrame和一个JButton(以放置框架,从而关闭该应用程序)。此帖子后面的代码。没什么特别的,只是为了澄清我们在说什么。我还

  • 我正在用MVC设计模式编写GUI程序。我有一个关于使用ActionListeners的问题。 根据MVC模式,所有ActionListeners都应该包含在Controller中。通常情况下,我相信它们会被实现到内部类中。 然而,如果有许多按钮等,那么将这些内部类移动到同一个包中的单独文件是一个好主意吗?我知道他们将不再是内部类,那么这是一个好的设计吗?由于我需要使用Controller类中的局部

  • 我想要小费。如果您有一个JFrame,其中有许多JPanels,这些JPanels有十个JTextFields标签和一个Edit按钮,那么您将如何构造您的应用程序。 您是将所有代码放在JFrame中,还是从JPanel扩展并将所有代码包含在JFrame中? 纽扣呢?我想使用MVC模式,您将如何处理ActionListeners?是每一个都有一个自己的控制器,还是你只使用一个控制器?

  • 问题内容: 我发现在“真实的Swing生活”中最难掌握的设计模式之一是MVC模式。我浏览了该站点上的很多文章,讨论了该模式,但是我仍然不太清楚如何在Java Swing应用程序中利用该模式。 假设我有一个JFrame,其中包含一个表,几个文本字段和一些按钮。我可能会使用TableModel将JTable与基础数据模型“桥接”。但是,负责清除字段,验证字段,锁定字段以及按钮操作的所有函数通常都直接在

  • 这是要根据模式验证的JSON。 问题是,如果我们传递了错误的数据,它将正确地验证eid和ename的类型(即整数或字符串)。对于例如: 如果我们为限定传递了错误的类型,那么它将验证为true(即,它不验证限定的类型,可能是因为它是嵌套的)。

  • 问题内容: 我知道负载测试应在非GUI模式下运行。 但是, 当我使用以下命令运行测试时: 在非GUI模式下 :我得到的平均响应时间为3到4秒,这当然是不可接受的。 在GUI模式下 :平均响应时间为100ms。 测试非常简单,仅需5秒即可完成100个用户的HTTP请求(GET)。 如果不是这样,我什么也不会说。 我应该信任哪一个? 我的问题更多:发生了什么,如何找到问题? 问题答案: 非GUI模式比