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

时间:2017-12-03 01:49:26

标签: java swing model-view-controller

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

到目前为止,这是我的View类:

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

代码有点错误,我会解决它,但我目前的优先事项是让我的按钮工作。如果控制器调用方法,我已经尝试制作将返回按钮的方法,但是我觉得这不是正确的方法。

3 个答案:

答案 0 :(得分:1)

我不知道 The Canonical Way ™这样做,但一种方法是使用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方法中触发属性更改方法来通知属性更改支持对象smvcButtons字段的更改:

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

现在将通知听众更改

在Controller中,我们有一个监听器:

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

答案 1 :(得分:0)

如果您只是在谈论添加动作监听器,那么您只需实现动作监听器,然后将其添加到按钮中......

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
      */
     }
   }

答案 2 :(得分:-2)

我无法记住它,但它看起来像这样:

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

或lamda方式:

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

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