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

我目前正在编写模板Java应用程序,并且如果我想完全遵循MVC模式,就不确定ActionListener的位置。

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

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

已经有一个以上类似的问题,例如:

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

  • 将所有ActionListeners放在单独的程序包中是否合理?
  • 我这样做是出于View和Controller尤其是可读性的考虑。如果有很多听众
  • 如果侦听器不是Controller内部的子类,则如何从ActionListener中执行Controller函数?(后续问题)

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

此致,

jaySon

控制器:

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

}

}

回答:

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

从技术上讲,模型和控制器应该可以交互,而控制器和视图应该可以交互,但是视图和模型永远不能交互,这显然不是Swing的工作原理,但这是另一个争论……

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

这建议ActionListener附加到UI控件的s应该由视图维护。然后,视图应警告控制器已发生某种动作。为此,你可以使用ActionListener控制器订阅的视图(由视图管理)。

更好的是,我将拥有一个专用的视图侦听器,该监听器描述了该视图可能产生的动作,例如…

public interface MainViewListener {

public void didPerformClose(MainView mainView);

}

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

即使在此示例中,我也很想创建一个“主视图”界面,该界面描述了任何实现都可以保证提供的属性(设置程序和获取器)和操作(侦听器/回调),那么你无需关心这些会发生一些动作,只有当他们这样做时,你才有望做某事…

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

更新…

让我们以这个为例…

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

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

该视图还希望知道何时进行认证,因为它将禁用其字段,因此用户在进行认证时无法更新视图,同样,它需要知道何时进行认证失败或成功,因为它将需要针对这些突发事件采取行动。

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

}

该CredentialsPane是一个物理实现CredentialsView,它实现了合同,但管理它自己的内部状态。合同的管理方式与控制者无关,它只关心合同是否得到维护…

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它将告诉视图何时进行认证以及认证失败还是成功。

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特殊之处在于,它充当的视图LoginViewController,但也充当的控制器CredentialsView。这很重要,因为没有任何说法说视图不能成为控制器,但是我会谨慎考虑如何实现这样的事情,因为这样做并非总是有意义,但是因为这两个视图是一起收集信息和管理事件,在这种情况下很有意义。

由于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();

}

以上是 Java和GUI-根据MVC模式,ActionListeners在哪里? 的全部内容, 来源链接: utcz.com/qa/406690.html

回到顶部