JTextField输入无法在MVC中更新TextView中的输出 [英] JTextField input fails to update output in TextView in MVC

查看:155
本文介绍了JTextField输入无法在MVC中更新TextView中的输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究高级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();

//构造函数
public MVCDemo(){

//设置布局并添加按钮
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 =您的学生ID#;

//事件触发机制使用的实用程序
private ArrayList< ActionListener> actionListenerList;

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

//通知侦听器文本更改
processEvent(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,text));
}

public String getText(){
return text;
}

//注册一个动作事件监听器
public synchronized void addActionListener(ActionListener l){
if(actionListenerList == null)
actionListenerList =新的ArrayList< ActionListener>();
}

//删除一个动作事件监听器
public synchronized void removeActionListener(ActionListener l){
if(actionListenerList!= null&&&ActionListenerList.contains (l))
actionListenerList.remove(l);
}

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

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

public class TextView扩展JPanel {
private TextModel model;

//设置一个模型
public void setModel(TextModel model){
this.model = model;

if(model!= null)
//将视图注册为模型的监听器
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 };
私有TextModel模型;
private JTextField jtfText = new JTextField();
private JComboBox jcboColorList = new JComboBox(colorStrings);

//构造函数
public TextController(){
//面板到组标签
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));

//面板分组文本字段,组合框和微调
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);

//注册监听器
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 组件(尚未找出如何执行 JComboBox JSpinner 正确),即使这样也不完美。



当我第一次启动程序并打开视图和控制面板时,视图中将显示您的学生ID#的默认字符串。但是当我在$ code> JTextField 中输入其他字符串并按回车键时, TextView 中的输出字符串不会更新,除非我关闭视图面板并重新打开。有人可以指出导致这种行为的原因吗?



我怀疑这可能与我的程序的事件处理有关。但是我仍然很新的GUI编程,并且对事件的触发和处理方式有非常基础的了解。所以,如果有人能以初学者友好的方式解释问题的根本原因,我将非常感激。

解决方案

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



在正式的MVC中,模型和视图不应该彼此了解,控制器用于将它们桥接在一起。 Swing并不遵循一个严格的MVC(它更多的是一个MVC),有时试图围绕它包装一个严格的MVC可能不会导致头痛。



我所做的是将MVC包装在Swing上,这意味着该视图不需要公开其UI元素,而是依赖于控制器和视图之间的合同来确定每个方可以执行的任何操作。 >

我们从一个例子开始。



从定义合同开始。这些应该是接口,因为它允许你以允许物理实现改变的方式去分离代码,而不影响API的其他部分,也许像...的一样。

  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>听众;

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实现TextController {

private TextModel model;
私有TextView视图;

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){
//你可以简单的在textWasChanged查看
//,并使视图询问控制器的值,但在哪里是
//在那里有趣: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扩展了JPanel实现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){
//我们不在乎,因为我们负责更改
// text anyway:P
}

}

public class OutputTextView扩展JPanel实现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();
}
返回大小;
}

@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();
//共享模型!
TextController inputController = new DefaultTextController(model,inputView);
TextController outputController = new DefaultTextController(model,outputView);

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



因为我知道复制单独的代码和stich他们在一起...



  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(){
尝试{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex){
ex.printStackTrace();
}

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

TextModel model = new DefaultTextModel();
//共享模型!!
TextController inputController = new DefaultTextController(model,inputView);
TextController outputController = new DefaultTextController(model,outputView);

JFrame frame = new JFrame(测试);
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实现TextModel {

private String text;
private Set< ChangeListener>听众;

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实现TextController {

private TextModel model;
私有TextView视图;

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){
//你可以简单的在textWasChanged查看
//,并使视图询问控制器的值,但在哪里是
//在那里有趣: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扩展JPanel实现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){
//我们不在乎,因为我们负责更改
// text anyway:P
}

}

public class OutputTextView扩展JPanel实现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();
}
返回大小;
}

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

}
}


I'm studying advanced Java and trying to write a program that utilizes the MVC design pattern. The program needs to draw a string which can be modified by a user's input in a JTextField. The user can also adjust the color and font size of the text through a JComboBox and a JSpinner respectively.

Here is what I have so far:

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

At this moment I've only implemented the JTextField component (yet to figure out how to do the JComboBox and JSpinner properly), and even that is hardly perfect.

When I first launch the program and turn on both view and controller panels, the default string of "Your Student ID #" is shown correctly in the view. But when I type some other string into the JTextField and hit enter, the output string in TextView does not update unless I close the view panel and reopen it. Could someone point out what is causing this behavior?

I suspect it probably has something to do with the event handling part of my program. But I'm still quite new to GUI programming and have a very basic understanding of how events are triggered and handled. So I would be really grateful if someone could explain the underlying cause of the problem in a beginner-friendly fashion.

解决方案

Your mixing your layers, both the model and controller are non visual entities. I'm in my phone so I've not inspected your code in any depth, but, your view should notify the controller (directly or in directly) when the values change, the controller will update the model accordingly which will notify the controller which will further notify the view

In a formal MVC, the model and view should never know about each other, the controller is used to bridge them together. Swing doesn't follow a strict MVC (it's more of a MV-C) and sometimes trying to wrap a strict MVC around it can cause no end of headaches.

Instead, what I do, is wrap the MVC around Swing, this means that the view doesn't need to expose its UI elements, but instead, relies on a contract between the controller and the view to determine what each party can do

Let's start with an example.

Start by defining the contracts. These should be interfaces, as it allows you to decouple the code in a way that allows the physical implementation to change without affecting the other parts of the API, maybe something like...

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

Now, normally, I'd consider making some abstract versions, to wrap up the common functionality, but for the sake of the example, I've jumped straight to the default implementations...

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

}

Now, you should be asking yourself, how is this all going to work, you have an input and an output view. The reality is, it will work really, really well, but first, we need the two different views...

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

}

These are both implementations of the TextView, the difference is, one view (the input) only sets the text and ignores changes to the text and one only responds to the changes in the text and never sets it...

Brain still not coping? Let me demonstrate....

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

Basically, here, we one two views, two controllers and ONE, shared, model. When the input side of things changes the text, the model notifies the output side of things and they are updated

And because I know what fun it is to copy separate pieces of code and stich them together...

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输入无法在MVC中更新TextView中的输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆