使用类属性的多个类似 DocumentListeners - 如何将它们压缩为一个侦听器?
Posted
技术标签:
【中文标题】使用类属性的多个类似 DocumentListeners - 如何将它们压缩为一个侦听器?【英文标题】:Multiple similar DocumentListeners using class properties - How to condense them to one listener? 【发布时间】:2016-04-07 13:30:51 【问题描述】:我有一个类TestListeners
,包含一些 JTextField 和其他属性(此处为整数)。我希望在相应的 JTextField 文本更改时更新属性。因此,我添加了一些 DocumentListener,但是这些监听器做的事情几乎完全相同,只是目标属性不同。目标是只有一个可以添加到所有三个 JTextField 的侦听器,并且应该为每个 JTextField 处理相应的类属性。
我发现了以下问题,它帮助我将 JTextField 动态地放入侦听器中,而无需在其中进行硬编码:
https://codereview.stackexchange.com/questions/25895/applying-documentlistener-on-multiple-jtextfield
但我找不到在侦听器中动态引用应该更改的属性的方法。如果有人可以帮助找到方法,我会很高兴。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class TestListeners extends JFrame
Color white = new Color(255, 255, 255);
Color red = new Color(255, 200, 200);
JTextField field1 = new JTextField("11111");
JTextField field2 = new JTextField("22222");
JTextField field3 = new JTextField("33333");
int value1 = 0;
int value2 = 0;
int value3 = 0;
public static void main(String[] args)
new TestListeners();
public TestListeners()
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(new Dimension(150, 100));
this.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx++; this.add(field1, c);
c.gridx++; this.add(field2, c);
c.gridx++; this.add(field3, c);
field1.getDocument().addDocumentListener(new DocumentListener()
@Override
public void removeUpdate(DocumentEvent e)
filterText(field1.getText());
@Override
public void insertUpdate(DocumentEvent e)
filterText(field1.getText());
@Override
public void changedUpdate(DocumentEvent e)
filterText(field1.getText());
private void filterText(String text)
try
value1 = Integer.parseInt(text);
field1.setBackground(white);
System.out.println("value1 = " + value1);
catch (NumberFormatException ex)
System.out.println("text in field1 is not an integer");
field1.setBackground(red);
);
field2.getDocument().addDocumentListener(new DocumentListener()
@Override
public void removeUpdate(DocumentEvent e)
filterText(field2.getText());
@Override
public void insertUpdate(DocumentEvent e)
filterText(field2.getText());
@Override
public void changedUpdate(DocumentEvent e)
filterText(field2.getText());
private void filterText(String text)
try
value2 = Integer.parseInt(text);
field2.setBackground(white);
System.out.println("value2 = " + value2);
catch (NumberFormatException ex)
System.out.println("text in field2 is not an integer");
field2.setBackground(red);
);
field3.getDocument().addDocumentListener(new DocumentListener()
@Override
public void removeUpdate(DocumentEvent e)
filterText(field3.getText());
@Override
public void insertUpdate(DocumentEvent e)
filterText(field3.getText());
@Override
public void changedUpdate(DocumentEvent e)
filterText(field3.getText());
private void filterText(String text)
try
value3 = Integer.parseInt(text);
field3.setBackground(white);
System.out.println("value3 = " + value3);
catch (NumberFormatException ex)
System.out.println("text in field3 is not an integer");
field3.setBackground(red);
);
this.setVisible(true);
【问题讨论】:
如果 Java 中像 C++ 中一样存在“引用调用”,则可以通过将value1
等的引用传递给侦听器来轻松解决此问题。
或者像你可以在 javascript 中做的那样:someObject["value1"] = 123;
【参考方案1】:
您正在使用匿名类来实现您的DocumentListner
。
你当然可以新建一个类
public class MyDocumentListener implements DocumentListner
public MyDocumentListener (TestListeners parentFrame)
// either via getter or in your example:
parentFrame.value3 = 123; // value3 is packed protected.
// copy paste your methods here.
您必须将要更改的 GUI 项目的引用传递给 MyDocumentListener
的新实例。
请注意,您有没有任何修饰符的成员变量。因此,该成员受到数据包保护。只要您的新课程与TestListeners
包装相同,您就可以在没有getter 的情况下访问它们。否则,为此值实现 getter 和 setter。
您可以通过实现适当的构造函数来做到这一点。
您可以像这样添加DocumentListner
:
field3.getDocument().addDocumentListener(new MyDocumentListner(this));
我们必须将“this”传递给 MyDocumentListner 的新实例,以便新的侦听器能够访问其中的字段。
或者您可以创建一个包含匿名类的对象:
DocumentListner myListener = new DoucmentListener
// implement your methods here
然后您可以像这样将DocumentListener
传递给您的字段:
field3.getDocument().addDocumentListener(myListener);
【讨论】:
这并不能解决问题。如果我将此侦听器添加到field3
(以您的为例),我将如何在侦听器内部引用TestListener
类的value3
?
正如我所写:您必须通过构造函数传递对 TestListener 的引用。我将更新我的答案以使这一点更清楚。
谢谢。但是将parentFrame.value3
放入侦听器中就像我已经在问题中所做的那样硬编码,因为MyDocumentListener
只适用于value3
,而不适用于其他。我必须创建其他类来使用 value1
和 value2
最终结果与匿名类的最终示例相同,只是这些类不再是匿名的。
由于您的设计,您将没有太多选择。您可以通过 DocumentEvent e => e.getSource() 访问 TextField。但是如果不使用反射或硬编码,您仍然无法将其与您的 int 值相关联。通过我给出的第一个方法,您可以扩展您的 Listener 以执行更具体的操作。实现与抽象父侦听器类似的行为。
如果可以帮助解决这个价值问题,我很乐意更改设计,但现在似乎有在 Java 中通过引用调用的方法。【参考方案2】:
首先关注侦听器之间的共同点,它们验证JTextField
的状态,但它们也保持最后一个有效值。
因此,您需要一个侦听器,它可以从 JTextField
获取文本,可以根据当前文本更改其状态,并且可以保持最后一个有效值,例如...
public class VerifyHandler implements DocumentListener
private int value;
private JTextField field;
public VerifyHandler(JTextField field)
this.field = field;
public int getValue()
return value;
@Override
public void insertUpdate(DocumentEvent e)
filterText();
@Override
public void removeUpdate(DocumentEvent e)
filterText();
@Override
public void changedUpdate(DocumentEvent e)
filterText();
private void filterText()
try
value = Integer.parseInt(field.getText());
field.setBackground(white);
catch (NumberFormatException ex)
field.setBackground(red);
现在,坦率地说,对于已经实现的东西来说,这似乎需要做很多工作。
例如,您可以使用JSpinner
或JFormattedTextField
或事件DocumentFilter
来防止用户输入您想要的以外的任何内容。
见:
How to Use Spinners How to Use Formatted Text Fields Implementing a Document Filter 和 DocumentFilter Examples更多详情
【讨论】:
我能不能问一些相关的问题,除了这个我刚刚想到的评估问题?我目前正在质疑每次视图发生变化时更新模型的合理性,而实际上只有当用户按下“开始”按钮时我才需要所有值。如果我从视图中获取值并将它们设置在模型中,就在用户点击“开始”时,它将为我节省数百行代码。有反对的理由吗? 不。我将视图用作值的中间容器,可用于向用户提供任何潜在问题的即时反馈(例如,我们知道该字段需要一个数字,但不是可接受的范围)以上是关于使用类属性的多个类似 DocumentListeners - 如何将它们压缩为一个侦听器?的主要内容,如果未能解决你的问题,请参考以下文章
如何告诉 SwiftUI 视图绑定到多个嵌套的 ObservableObject