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

JTextField输入无法更新MVC中TextView中的输出

沈自珍
2023-03-14

我正在学习高级Java并尝试编写一个利用MVC设计模式的程序。程序需要绘制一个字符串,用户可以在JTextField中输入该字符串进行修改。用户还可以分别通过一个JComboBox和一个JSpinner来调整文本的颜色和字体大小。

以下是我目前掌握的情况:

public class MVCDemo extends JApplet {
    private JButton jBtnController = new JButton("Show Controller");
    private JButton jBtnView = new JButton("Show View");
    private TextModel model = new TextModel();

//constructor
public MVCDemo(){

    //set layout and add buttons
    setLayout(new FlowLayout());
    add(jBtnController);
    add(jBtnView);

    jBtnController.addActionListener(new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent e){
            JFrame frame = new JFrame("Controllor");
            TextController controller = new TextController();
            controller.setModel(model);
            frame.add(controller);
            frame.setSize(200, 100);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    });

    jBtnView.addActionListener(new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent e){
            JFrame frame = new JFrame("View");
            TextView view = new TextView();
            view.setModel(model);
            frame.add(view);
            frame.setSize(500, 200);
            frame.setLocation(200, 200);
            frame.setVisible(true);
        }
    });
}

    public static void main(String[] args){
        MVCDemo applet = new MVCDemo();
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("MVCDemo");
        frame.getContentPane().add(applet, BorderLayout.CENTER);
        frame.setSize(400, 90);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

public class TextModel {   

 private String text = "Your Student ID #";

    //utility field used by event firing mechanism
    private ArrayList<ActionListener> actionListenerList;

    public void setText(String text){
        this.text = text;

        //notify the listener for the change on text
        processEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "text"));
    }

    public String getText(){
        return text;
    }

    //register an action event listener
    public synchronized void addActionListener(ActionListener l){
        if (actionListenerList == null)
            actionListenerList = new ArrayList<ActionListener>();
    }

    //remove an action event listener
    public synchronized void removeActionListener(ActionListener l){
        if (actionListenerList != null && actionListenerList.contains(l))
            actionListenerList.remove(l);
    }

    //fire TickEvent
    private void processEvent(ActionEvent e){
        ArrayList<ActionListener> list;

        synchronized (this){
            if (actionListenerList == null)
                return;
            list = (ArrayList<ActionListener>)(actionListenerList.clone());
        }
    }
}

public class TextView extends JPanel{
    private TextModel model;

    //set a model
    public void setModel(TextModel model){
        this.model = model;

        if (model != null)
            //register the view as listener for the model
            model.addActionListener(new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent e){
                    repaint();
                }
            });
    }

    public TextModel getModel(){
        return model;
    }

    @Override
    public void paintComponent(Graphics g){
        if (model != null){
            super.paintComponent(g);
            //g.setColor(model.getColor());

            g.drawString(model.getText(), 190, 90);
        }
    }
}

public class TextController extends JPanel {
    String[] colorStrings = { "Black", "Blue", "Red" };
    private TextModel model;
    private JTextField jtfText = new JTextField();
    private JComboBox jcboColorList = new JComboBox(colorStrings);

    //constructor
    public TextController(){
        //panel to group labels
        JPanel panel1 = new JPanel();
        panel1.setLayout(new GridLayout(3, 1));
        panel1.add(new JLabel("Text"));
        panel1.add(new JLabel("Color"));
        panel1.add(new JLabel("Size"));

        //panel to group text field, combo box and spinner
        JPanel panel2 = new JPanel();
        panel2.setLayout(new GridLayout(3, 1));
        panel2.add(jtfText);
        panel2.add(jcboColorList);

        setLayout(new BorderLayout());
        add(panel1, BorderLayout.WEST);
        add(panel2, BorderLayout.CENTER);

        //register listeners
        jtfText.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e){
                if (model != null)
                    model.setText(jtfText.getText());
            }
        });

        /*jcboColorList.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e){
                if (model != null)
                    model.set
            }
        });*/
    }

    public void setModel(TextModel model){
        this.model = model;
    }

    public TextModel getModel(){
        return model;
    }
}

目前,我只实现了JTextField组件(还没有弄清楚如何正确地执行JComboBoxJSpinner),即使这样也很难做到完美。

我怀疑这可能与我程序中的事件处理部分有关。但我对GUI编程还是一个新手,对事件是如何触发和处理的有一个非常基本的了解。因此,如果有人能以一种初学者友好的方式解释问题的根本原因,我将不胜感激。

共有1个答案

鄢禄
2023-03-14

你混合你的层,模型和控制器都是非视觉实体。我在我的电话里,所以我没有深入地检查您的代码,但是,您的视图应该通知控制器(直接或直接)当值改变时,控制器将相应地更新模型,该模型将通知控制器,控制器将进一步通知视图

在一个正式的MVC中,模型和视图不应该互相了解,控制器是用来将它们连接在一起的。Swing并不遵循严格的MVC(它更像是一个MV-C),有时试图将严格的MVC封装在其周围可能会导致无休止的头痛。

相反,我所做的是将MVC封装在Swing周围,这意味着视图不需要公开其UI元素,而是依赖于控制器和视图之间的契约来确定各方可以做什么

让我们从一个例子开始。

public interface TextModel {
    public void setText(String text);
    public String getText();
    public void addChangeListener(ChangeListener listener);
    public void removeChangeListener(ChangeListener listener);
}

public interface TextController {
    public String getText();
    public void setText(String text);
}

public interface TextView {
    public TextController getController();
    public void setController(TextController controller);
    public void setText(String text);
}
public class DefaultTextModel implements TextModel {

    private String text;
    private Set<ChangeListener> listeners;

    public DefaultTextModel() {
        listeners = new HashSet<>(25);
    }

    @Override
    public String getText() {
        return text;
    }

    @Override
    public void setText(String value) {
        if (text == null ? value != null : !text.equals(value)) {
            this.text = value;
            fireStateChanged();
        }
    }

    @Override
    public void addChangeListener(ChangeListener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeChangeListener(ChangeListener listener) {
        listeners.remove(listener);
    }

    protected void fireStateChanged() {
        ChangeListener[] changeListeners = listeners.toArray(new ChangeListener[0]);
        if (changeListeners != null && changeListeners.length > 0) {
            ChangeEvent evt = new ChangeEvent(this);
            for (ChangeListener listener : changeListeners) {
                listener.stateChanged(evt);
            }
        }
    }

}

public class DefaultTextController implements TextController {

    private TextModel model;
    private TextView view;

    public DefaultTextController(TextModel model, TextView view) {
        this.model = model;
        this.view = view;

        this.view.setController(this);
        this.model.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                // You could simply make a "textWasChanged" method on the view
                // and make the view ask the controller for the value, but where's
                // the fun in that :P
                getView().setText(getText());
            }
        });
    }

    public TextModel getModel() {
        return model;
    }

    public TextView getView() {
        return view;
    }

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

    @Override
    public void setText(String text) {
        getModel().setText(text);
    }

}
public class InputTextView extends JPanel implements TextView {

    private TextController controller;

    public InputTextView() {
        setLayout(new GridBagLayout());
        JTextField field = new JTextField(10);
        add(field);
        field.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getController().setText(field.getText());
            }
        });
    }

    @Override
    public TextController getController() {
        return controller;
    }

    @Override
    public void setController(TextController controller) {
        this.controller = controller;
    }

    @Override
    public void setText(String text) {
        // We kind of don't care, because we're responsible for changing the
        // text anyway :P
    }

}

public class OutputTextView extends JPanel implements TextView {

    private TextController controller;

    public OutputTextView() {
    }

    @Override
    public TextController getController() {
        return controller;
    }

    @Override
    public void setController(TextController controller) {
        this.controller = controller;
    }

    @Override
    public void setText(String text) {
        revalidate();
        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension size = new Dimension(200, 40);
        TextController controller = getController();
        if (controller != null) {
            String text = controller.getText();
            FontMetrics fm = getFontMetrics(getFont());
            if (text == null || text.trim().isEmpty()) {
                size.width = fm.stringWidth("M") * 10;
            } else {
                size.width = fm.stringWidth(text);
            }
            size.height = fm.getHeight();
        }
        return size;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        TextController controller = getController();
        String text = "";
        if (controller != null) {
            text = controller.getText();
        }
        if (text == null) {
            text = "";
        }
        FontMetrics fm = g.getFontMetrics();
        int x = (getWidth() - fm.stringWidth(text)) / 2;
        int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
        g.drawString(text, x, y);
    }

}

这两个都是textview的实现,不同的是,一个视图(输入)只设置文本并忽略对文本的更改,另一个视图只响应文本中的更改而从不设置它...

大脑还是不能应付?我来演示一下....

InputTextView inputView = new InputTextView();
OutputTextView outputView = new OutputTextView();

TextModel model = new DefaultTextModel();
// Shared model!!
TextController inputController = new DefaultTextController(model, inputView);
TextController outputController = new DefaultTextController(model, outputView);

基本上,在这里,我们有两个视图,两个控制器和一个共享模型。当事物的输入端改变文本时,模型通知输出端事物,并且它们被更新

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Test {

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

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

                InputTextView inputView = new InputTextView();
                OutputTextView outputView = new OutputTextView();

                TextModel model = new DefaultTextModel();
                // Shared model!!
                TextController inputController = new DefaultTextController(model, inputView);
                TextController outputController = new DefaultTextController(model, outputView);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridLayout(2, 0));
                frame.add(inputView);
                frame.add(outputView);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface TextModel {
        public void setText(String text);
        public String getText();
        public void addChangeListener(ChangeListener listener);
        public void removeChangeListener(ChangeListener listener);
    }

    public interface TextController {
        public String getText();
        public void setText(String text);
    }

    public interface TextView {
        public TextController getController();
        public void setController(TextController controller);
        public void setText(String text);
    }

    public class DefaultTextModel implements TextModel {

        private String text;
        private Set<ChangeListener> listeners;

        public DefaultTextModel() {
            listeners = new HashSet<>(25);
        }

        @Override
        public String getText() {
            return text;
        }

        @Override
        public void setText(String value) {
            if (text == null ? value != null : !text.equals(value)) {
                this.text = value;
                fireStateChanged();
            }
        }

        @Override
        public void addChangeListener(ChangeListener listener) {
            listeners.add(listener);
        }

        @Override
        public void removeChangeListener(ChangeListener listener) {
            listeners.remove(listener);
        }

        protected void fireStateChanged() {
            ChangeListener[] changeListeners = listeners.toArray(new ChangeListener[0]);
            if (changeListeners != null && changeListeners.length > 0) {
                ChangeEvent evt = new ChangeEvent(this);
                for (ChangeListener listener : changeListeners) {
                    listener.stateChanged(evt);
                }
            }
        }

    }

    public class DefaultTextController implements TextController {

        private TextModel model;
        private TextView view;

        public DefaultTextController(TextModel model, TextView view) {
            this.model = model;
            this.view = view;

            this.view.setController(this);
            this.model.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    // You could simply make a "textWasChanged" method on the view
                    // and make the view ask the controller for the value, but where's
                    // the fun in that :P
                    getView().setText(getText());
                }
            });
        }

        public TextModel getModel() {
            return model;
        }

        public TextView getView() {
            return view;
        }

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

        @Override
        public void setText(String text) {
            getModel().setText(text);
        }

    }

    public class InputTextView extends JPanel implements TextView {

        private TextController controller;

        public InputTextView() {
            setLayout(new GridBagLayout());
            JTextField field = new JTextField(10);
            add(field);
            field.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    getController().setText(field.getText());
                }
            });
        }

        @Override
        public TextController getController() {
            return controller;
        }

        @Override
        public void setController(TextController controller) {
            this.controller = controller;
        }

        @Override
        public void setText(String text) {
            // We kind of don't care, because we're responsible for changing the
            // text anyway :P
        }

    }

    public class OutputTextView extends JPanel implements TextView {

        private TextController controller;

        public OutputTextView() {
        }

        @Override
        public TextController getController() {
            return controller;
        }

        @Override
        public void setController(TextController controller) {
            this.controller = controller;
        }

        @Override
        public void setText(String text) {
            revalidate();
            repaint();
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension size = new Dimension(200, 40);
            TextController controller = getController();
            if (controller != null) {
                String text = controller.getText();
                FontMetrics fm = getFontMetrics(getFont());
                if (text == null || text.trim().isEmpty()) {
                    size.width = fm.stringWidth("M") * 10;
                } else {
                    size.width = fm.stringWidth(text);
                }
                size.height = fm.getHeight();
            }
            return size;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            TextController controller = getController();
            String text = "";
            if (controller != null) {
                text = controller.getText();
            }
            if (text == null) {
                text = "";
            }
            FontMetrics fm = g.getFontMetrics();
            int x = (getWidth() - fm.stringWidth(text)) / 2;
            int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g.drawString(text, x, y);
        }

    }
}
 类似资料:
  • 我试图限制用户可以在文本字段中输入的字符的最大长度,但似乎不起作用。 以下是代码: 我做错什么了吗?我怎样才能使限制正常工作?

  • 所以我做了一个测试窗口,里面有一个JTextField。我不知道出了什么问题。下面是主要代码。问题是,无论我做什么,我都无法编辑文本字段,也无法编辑我创建的第二个文本字段。我有一个带有文本字段的示例程序,它也可以工作,但根本不工作。 我不确定是否需要发布它,但我可以在这里获得完整程序的示例罐。我只发布了处理文本字段的区域 编辑:完整的源代码可在此处获得:GITHUB 我移除了一些东西,它成功了,我

  • 问题内容: 因此,您可能知道,如果您有一个文本字段并向其中添加一个ActionListener,它将仅侦听Enter键的按键操作。但是,我想让我的ActionListener监听文本的更改。所以基本上我有这个: 有什么方法可以收听文本的更改(例如actionPerformed事件中记录的内容)? 问题答案: 来自@JR的回答 使用基础文档:

  • 我有一个Calendarview和一个EditText。我希望以DD/MM/YYYY格式在edittext中输入的日期,并将其设置在CalendarView上。视图应将当前日期气泡更新为设置的日期。 提前道谢!

  • 问题内容: 我正在尝试我的React.js的第一部分,并在很早的时候就陷入了困境…我有下面的代码,该代码将搜索表单呈现为。但是,在搜索框中输入内容无济于事。 大概在通过道具并上下移动时会丢失一些东西,这似乎是一个常见问题。但是我很沮丧-我看不到缺少的东西。 (最终,我会使用其他类型的,但是我只是想让这一类正常工作。) 问题答案: 您尚未将道具放在机箱中。它必须在JSX中。 在文档中充分考虑了将 p

  • 我正在学习反应和反应形式,并试图创建一个动态和可扩展的形式。我已经设置了具有的状态,并基于该类型,显示相应的输入类型(因此它可以是文本、文本区域、单选、选择、日期或复选框)。我试图写一个函数,这样它将动态设置基于的表单输入,但我卡住了试图实现相同的变化。 所以如果,或,等等。 请检查此工作代码沙盒。 看看这个完整的代码片段:- 我仍在努力学习并花时间与ReactJS在一起,因此任何帮助都将不胜感激