gpt4 book ai didi

java - Java和GUI-根据MVC模式,ActionListeners在哪里?

转载 作者:IT老高 更新时间:2023-10-28 21:01:09 25 4
gpt4 key购买 nike

我目前正在编写一个模板Java应用程序,并且如果我想完全遵循MVC模式,就不确定以何种方式接收ActionListeners。

该示例基于Swing,但与框架无关,而是与Java中使用任何框架创建GUI的MVC的基本概念有关。

我从一个绝对简单的应用程序开始,该应用程序包含一个JFrame和一个JButton(以放置框架,从而关闭该应用程序)。此帖子后面的代码。没什么特别的,只是为了澄清我们在说什么。我还没有开始使用Model,因为这个问题困扰了我太多。

已经有一个以上类似的问题,例如:
MVC pattern with many ActionListeners
Java swing - Where should the ActionListener go?

但是我想知道两件事,但没有一个让我真正满意:

  • 将所有ActionListener放在单独的程序包中是否合理?
  • 我想这样做的目的是为了使View and Controller尤其是View和Controller更具可读性。如果有很多听众
  • 如果侦听器不是Controller内部的子类,如何在ActionListener中执行Controller函数? (后续问题)

  • 我希望这不是我在这里问的太笼统或含糊,但这让我想了一会儿。我总是以自己的方式使用 ,让ActionHandler知道Controller的,但这似乎不对,所以我最后想知道如何正确完成。

    亲切的问候,
    杰森

    Controller :
    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();
    }
    }
    }

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

    最佳答案

    对于Swing来说,这是一个很难回答的问题,因为Swing并不是纯MVC实现,因此 View 和 Controller 是混合的。

    从技术上讲,模型和 Controller 应该能够交互,而 Controller 和 View 应该能够交互,但是 View 和模型永远都不能交互,这显然不是Swing的工作原理,但这是另一个争论……

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

    这表明,应该由 View 维护附加到UI控件的ActionListener。然后, View 应警告 Controller 已发生某种 Action 。为此,您可以使用由 View 管理的 Controller 订阅的另一个ActionListener

    更好的是,我将拥有一个专用的 View 侦听器,该监听器描述了该 View 可能产生的 Action ,例如...

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

    然后, Controller 将通过此侦听器订阅该 View ,并且在(在这种情况下)按下关闭按钮时,该 View 将调用 didPerformClose

    即使在此示例中,我也很想创建一个“主 View ”界面,该界面描述了任何实现都可以保证提供的属性(设置者和获取者)和操作(侦听器/回调),然后您就不在乎这些会发生一些 Action ,只有当他们这样做时,您才有望做某事...

    您想问自己,在每个级别上,为另一个实例更改任何元素(更改模型, Controller 或 View )有多容易?如果发现自己必须解耦代码,那么您就遇到了问题。通过接口(interface)进行通信,并尝试减少各层之间的耦合量以及每一层对其他层的了解程度,以至于它们仅维护契约(Contract)即可

    更新了...

    让我们以这个为例...

    实际上有两个 View (为实际对话框打折),有凭据 View 和登录 View ,是的,您将看到它们是不同的。

    凭证查看

    凭证 View 负责收集要验证的详细信息,用户名和密码。它将向 Controller 提供信息,以便在更改那些凭据时通知它,因为 Controller 可能需要采取一些措施,例如启用“登录”按钮...

    该 View 还希望知道何时进行认证,因为它将要禁用其字段,因此用户无法在进行认证时更新 View ,同样,它需要知道何时进行认证失败或成功,因为它将需要针对这些突发事件采取行动。
    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);

    }

    凭证 Pane
    CredentialsPaneCredentialsView的物理实现,它实现契约(Contract),但管理其自己的内部状态。契约(Contract)的管理方式与控制者无关,它只关心契约(Contract)是否得到维护...
    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;
    }

    }

    登录查看
    LoginView负责管理 CredentialsView,还负责通过某种方式在应进行身份验证或用户取消该过程时通知 LoginViewController

    同样, LoginViewController将告诉 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);

    }

    登录 Pane
    LoginPane有点特殊,它充当 LoginViewController的 View ,但也充当 CredentialsView的 Controller 。这很重要,因为没有任何说法说 View 不能成为 Controller ,但是我会谨慎考虑如何实现此类操作,因为这样做并非总是有意义,但因为这两个 View 是一起收集信息和管理事件,在这种情况下很有意义。

    由于 LoginPane将需要根据 CredentialsView的更改来更改其自身的状态,因此在这种情况下允许 LoginPane充当 Controller 是有意义的,否则,您需要提供更多的方法来控制按钮,但这开始将UI逻辑流到 Controller 上...
    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 - Java和GUI-根据MVC模式,ActionListeners在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26517856/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com