在Java中使用MVC实现多个按钮

在Java中使用MVC实现多个按钮,java,swing,model-view-controller,Java,Swing,Model View Controller,我正在制作一个在java中使用MVC的程序。我的问题是,我不知道如何实现ActionListeners和处理事件,例如单击按钮时。我知道代码应该在控制器中,但我不知道如何实现它。到目前为止,我已经制作了几个按钮,我想让它们在被点击时真正做些什么。我应该向视图中添加什么代码并在控制器类中编写 以下是我迄今为止的观点类: package decryptor; import java.awt.event.ActionEvent; import java.awt.event.ActionListene

我正在制作一个在java中使用MVC的程序。我的问题是,我不知道如何实现ActionListeners和处理事件,例如单击按钮时。我知道代码应该在控制器中,但我不知道如何实现它。到目前为止,我已经制作了几个按钮,我想让它们在被点击时真正做些什么。我应该向视图中添加什么代码并在控制器类中编写

以下是我迄今为止的观点类:

package decryptor;

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

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import java.io.File;

public class View
{
    private JFrame frame;

    private JPanel mainPanel;

    private JLabel instructions;
    private JLabel frequenciesFileLoadedLabel;
    private JLabel cipherFileLoadedLabel;

    private JButton frequenciesFileLoadButton;
    private JButton cipherFileLoadButton;

    private JButton decipherByRankButton;
    private JButton decipherByNearestFrequencyButton;

    private boolean cipherLoaded;
    private boolean frequenciesLoaded;

    public enum Buttons
    {
        FREQUENCIES_LOAD, CIPHER_LOAD, DECIPHER_RANK, DECIPHER_NEAREST;
    }

    public View()
    {
        cipherLoaded = false;
        frequenciesLoaded = false;

        frame = new JFrame("Text Decrypter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);

        mainPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
        mainPanel.setPreferredSize(new Dimension(100,400));

        frequenciesFileLoadedLabel = new JLabel("No file with character frequencies loaded");
        cipherFileLoadedLabel = new JLabel("No file to decipher loaded");

        frequenciesFileLoadButton = new JButton("Load character frequencies file");
        cipherFileLoadButton = new JButton("Load file to decipher");
        decipherByRankButton = new JButton("Decipher file by rank of character frequencies");
        decipherByRankButton.setEnabled(false);
        decipherByNearestFrequencyButton = new JButton("Decipher file by the nearest character frequency");
        decipherByNearestFrequencyButton.setEnabled(false);

        mainPanel.add(frequenciesFileLoadButton);
        mainPanel.add(frequenciesFileLoadedLabel);
        mainPanel.add(cipherFileLoadButton);
        mainPanel.add(cipherFileLoadedLabel);
        mainPanel.add(decipherByRankButton);
        mainPanel.add(decipherByNearestFrequencyButton);

        frame.add(mainPanel);
        frame.pack();
        frame.setVisible(true);

        //label fonts etc
    }

    private void enableButtons()
    {
        if (cipherLoaded == true && frequenciesLoaded == true)
        {
            decipherByRankButton.setEnabled(true);
            decipherByNearestFrequencyButton.setEnabled(true);
        }
    }

    private File loadFile()
    {
        JFileChooser fileChooser = new JFileChooser();

        fileChooser.setCurrentDirectory(new File( System.getProperty("user.home")));

        int fileChooserResult = fileChooser.showOpenDialog(frame);

        if (fileChooserResult == JFileChooser.APPROVE_OPTION)
        {
            return fileChooser.getSelectedFile();
        }
        else
        {
            JOptionPane.showMessageDialog(frame, "Error loading file. Please try again.", "Error", JOptionPane.ERROR_MESSAGE);

            return null;
        }
    }

    public File loadFrequencies()
    {
        File loadedFile = loadFile();

        if (loadedFile == null)
        {
            frequenciesLoaded = false;
            enableButtons();
            frequenciesFileLoadedLabel.setText("No file with character frequencies loaded");
            return loadedFile;
        }
        else
        {
            frequenciesLoaded = true;
            enableButtons();
            frequenciesFileLoadedLabel.setText("Character frequencies file loaded");
            return loadedFile;
        }
    }

    public File loadCiphered()
    {
        File loadedFile = loadFile();

        if (loadedFile == null)
        {
            cipherLoaded = false;
            enableButtons();
            cipherFileLoadedLabel.setText("No file to decipher loaded");
            return loadedFile;
        }
        else
        {
            cipherLoaded = true;
            enableButtons();
            cipherFileLoadedLabel.setText("File to decipher loaded");
            return loadedFile;
        }
    }

    public void fileOutput()
    {
        JOptionPane.showMessageDialog(frame, "File deciphered and output to file \"output.txt\".");
    }

    public JButton getButton(Buttons button)
    {
        switch (button) 
        {
            case FREQUENCIES_LOAD: return frequenciesFileLoadButton;
            case CIPHER_LOAD: return cipherFileLoadButton;
            case DECIPHER_RANK: return decipherByRankButton;
            case DECIPHER_NEAREST: return decipherByNearestFrequencyButton;
            default: return null;
        }
    }
}

这段代码有一点错误,我会修复它,但是我目前的首要任务是让我的按钮正常工作。我已经尝试过在控制器调用该方法时返回按钮的方法,但是我觉得这不是正确的方法。

我记不清了,但看起来是这样的:

btn.setOnclickListener(new OnclickListener(){
    ...// Override some methods to implement your business code.
}
);
或是南达道:

btn.setOnclickListener(()->{
    ....// Override some methods to implement your business code.
});

我相信您的ide具有一些自动修复功能,可以帮助您完成这些代码。:)

如果您只想添加一个动作监听器,那么只需实现动作监听器,然后将其添加到按钮中

public class View implements ActionListener{

 cipherFileLoadButton.addActionListener(this);
 // etc listener to all buttons
 }

 @Override
 public void actionPerformed(ActionEvent e) {

    Object o = e.getSource();

   if(o == cipherFileLoadButton){
      /*
       * Do stuff
      */
     }
   }

我不知道规范的方法™ 为此,一种方法是使用Swing自己的属性更改支持和PropertyChangeListeners作为一种机制来通知侦听器视图和模型的更改。例如,假设我们创建了您的枚举,并进行了修改:

// smvc for "simple model view controller"
public enum SmvcButtons {
    FREQUENCIES_LOAD("Load Frequencies"), 
    CIPHER_LOAD("Load Cipher"), 
    DECIPHER_RANK("Decipher Rank"), 
    DECIPHER_NEAREST("Decipher Nearest");
    private String text;

    private SmvcButtons(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}
并将其用于某种模型中:

public class SmvcModel {
    // constant for our single property name
    public static final String SMVC_BUTTONS = "smvc buttons";
    // a more complex model will have multiple "bound" properties

    // the support object will register listeners on the model
    // and will notify them of changes in model state
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
    private SmvcButtons smvcButtons;


    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    public SmvcButtons getSmvcButtons() {
        return smvcButtons;
    }

    public void setSmvcButtons(SmvcButtons smvcButtons) {
        SmvcButtons oldValue = null;
        SmvcButtons newValue = smvcButtons;
        this.smvcButtons = smvcButtons;
        pcSupport.firePropertyChange(SMVC_BUTTONS, oldValue, newValue);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(name, listener);
    }
}
在这里,我们通过在setter方法中触发property change方法来通知属性更改支持对象对smvcButtons字段的更改:

    public void setSmvcButtons(SmvcButtons smvcButtons) {
        SmvcButtons oldValue = null;
        SmvcButtons newValue = smvcButtons;
        this.smvcButtons = smvcButtons;
        pcSupport.firePropertyChange(SMVC_BUTTONS, oldValue, newValue);
    }
现在将通知侦听器更改

在控制器中,我们有一个侦听器,用于侦听以下内容:

private class ModelListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        String text = ((SmvcButtons) evt.getNewValue()).getText() + "\n";
        view.appendTextAreaText(text);
    }
}
假设我们有一个视图(保持简单只是为了说明),它使用枚举创建按钮,并使用类似的机制通知侦听器状态的更改:

@SuppressWarnings("serial")
public class SmvcView extends JPanel {
    public static final String SMVC_BUTTONS = "smvc buttons";
    private JTextArea textArea = new JTextArea(30, 50);

    public SmvcView() {
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        JPanel btnPanel = new JPanel(new GridLayout(1, 0, 3, 0));
        for (final SmvcButtons smvcBtn : SmvcButtons.values()) {
            JButton button = new JButton(smvcBtn.getText());
            btnPanel.add(button);
            button.addActionListener(e -> {
                Object oldValue = null;
                Object newValue = smvcBtn;
                firePropertyChange(SMVC_BUTTONS, oldValue, newValue);
            });
        }

        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
        setLayout(new BorderLayout(3, 3));
        add(scrollPane);
        add(btnPanel, BorderLayout.PAGE_END);
    }

    public void appendTextAreaText(String text) {
        textArea.append(text);
    }
}
请注意,视图将匿名侦听器附加到其按钮上,它们所做的唯一事情就是通知组件的固有属性更改支持按钮已被按下——就是这样。目标是使视图尽可能保持沉默:

button.addActionListener(e -> {
    Object oldValue = null;
    Object newValue = smvcBtn;
    firePropertyChange(SMVC_BUTTONS, oldValue, newValue);
});
然后,控制器可以使用类似的侦听器来侦听视图的更改:

private class ViewListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        model.setSmvcButtons((SmvcButtons) evt.getNewValue());
    }
}
上述侦听器所做的全部工作是基于按钮按下更改模型的状态(同样,此示例非常简单)

整个事情可能看起来像:

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class SimpleMvc {

    private static void createAndShowGui() {
        SmvcModel model = new SmvcModel();
        SmvcView view = new SmvcView();
        new Controller(model, view);

        JFrame frame = new JFrame("SimpleMvc");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(view);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

enum SmvcButtons {
    FREQUENCIES_LOAD("Load Frequencies"), 
    CIPHER_LOAD("Load Cipher"), 
    DECIPHER_RANK("Decipher Rank"), 
    DECIPHER_NEAREST("Decipher Nearest");
    private String text;

    private SmvcButtons(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

class Controller {

    private SmvcModel model;
    private SmvcView view;

    public Controller(SmvcModel model, SmvcView view) {
        this.model = model;
        this.view = view;

        model.addPropertyChangeListener(SmvcModel.SMVC_BUTTONS, new ModelListener());
        view.addPropertyChangeListener(SmvcView.SMVC_BUTTONS, new ViewListener());
    }

    private class ModelListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            String text = ((SmvcButtons) evt.getNewValue()).getText() + "\n";
            view.appendTextAreaText(text);
        }
    }

    private class ViewListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            model.setSmvcButtons((SmvcButtons) evt.getNewValue());
        }
    }

}

class SmvcModel {
    public static final String SMVC_BUTTONS = "smvc buttons";
    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
    private SmvcButtons smvcButtons;


    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(listener);
    }

    public SmvcButtons getSmvcButtons() {
        return smvcButtons;
    }

    public void setSmvcButtons(SmvcButtons smvcButtons) {
        SmvcButtons oldValue = null;
        SmvcButtons newValue = smvcButtons;
        this.smvcButtons = smvcButtons;
        pcSupport.firePropertyChange(SMVC_BUTTONS, oldValue, newValue);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.addPropertyChangeListener(name, listener);
    }

    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
        pcSupport.removePropertyChangeListener(name, listener);
    }
}

@SuppressWarnings("serial")
class SmvcView extends JPanel {
    public static final String SMVC_BUTTONS = "smvc buttons";
    private JTextArea textArea = new JTextArea(30, 50);

    public SmvcView() {
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        JPanel btnPanel = new JPanel(new GridLayout(1, 0, 3, 0));
        for (final SmvcButtons smvcBtn : SmvcButtons.values()) {
            JButton button = new JButton(smvcBtn.getText());
            btnPanel.add(button);
            button.addActionListener(e -> {
                Object oldValue = null;
                Object newValue = smvcBtn;
                firePropertyChange(SMVC_BUTTONS, oldValue, newValue);
            });
        }

        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
        setLayout(new BorderLayout(3, 3));
        add(scrollPane);
        add(btnPanel, BorderLayout.PAGE_END);
    }

    public void appendTextAreaText(String text) {
        textArea.append(text);
    }
}   

这里的
btn
是按钮对象Swing JButton中没有
setOnClickLIstener(…)
方法。我检查了以前的代码文件,它应该像btn.addActionListener(…),但这个问题不是关于如何将侦听器添加到JButton,而是如何将按钮连接到有效的MVC设计结构中,因此,即使您修改了答案并更正了代码,答案仍然是不正确的。您可能希望删除它。重用现有功能的好方法亲爱的@HovercraftFullOfEels,JFrame代码与视图部分不相关吗?它不应该包含在SmvcView类的某个地方吗?我有点困惑如何在现实世界中正确地实现这个模式。“我知道代码应该在控制器中”,这是试图在现有MVC(Swing就是这样)的基础上包装另一个MVC的核心问题。事实上,除了视图契约提供的功能外,控制器实际上没有责任干扰UI。这将使控制器与视图分离,并使设计视图的多个实现成为可能,单个控制器可以对其进行控制。这还意味着实现细节(如向按钮注册
ActionListener
s)不是控制器的责任,但这是观点的责任。该视图将通过一个定义的constrap向控制器进行通信更改,并且有2个或更多按钮,那么我是否为每个按钮创建一个单独的类来实现ActionListener?