我正在学习高级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
组件(还没有弄清楚如何正确地执行JComboBox
和JSpinner
),即使这样也很难做到完美。
我怀疑这可能与我程序中的事件处理部分有关。但我对GUI编程还是一个新手,对事件是如何触发和处理的有一个非常基本的了解。因此,如果有人能以一种初学者友好的方式解释问题的根本原因,我将不胜感激。
你混合你的层,模型和控制器都是非视觉实体。我在我的电话里,所以我没有深入地检查您的代码,但是,您的视图应该通知控制器(直接或直接)当值改变时,控制器将相应地更新模型,该模型将通知控制器,控制器将进一步通知视图
在一个正式的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在一起,因此任何帮助都将不胜感激