使用类属性的多个类似 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,而不适用于其他。我必须创建其他类来使用 value1value2 最终结果与匿名类的最终示例相同,只是这些类不再是匿名的。 由于您的设计,您将没有太多选择。您可以通过 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);
        
    


现在,坦率地说,对于已经实现的东西来说,这似乎需要做很多工作。

例如,您可以使用JSpinnerJFormattedTextField 或事件DocumentFilter 来防止用户输入您想要的以外的任何内容。

见:

How to Use Spinners How to Use Formatted Text Fields Implementing a Document Filter 和 DocumentFilter Examples

更多详情

【讨论】:

我能不能问一些相关的问题,除了这个我刚刚想到的评估问题?我目前正在质疑每次视图发生变化时更新模型的合理性,而实际上只有当用户按下“开始”按钮时我才需要所有值。如果我从视图中获取值并将它们设置在模型中,就在用户点击“开始”时,它将为我节省数百行代码。有反对的理由吗? 不。我将视图用作值的中间容器,可用于向用户提供任何潜在问题的即时反馈(例如,我们知道该字段需要一个数字,但不是可接受的范围)

以上是关于使用类属性的多个类似 DocumentListeners - 如何将它们压缩为一个侦听器?的主要内容,如果未能解决你的问题,请参考以下文章

WPf:一次绑定多个属性

如何告诉 SwiftUI 视图绑定到多个嵌套的 ObservableObject

使用相同的函数设置多个类属性

Hystrix断路器配置属性解析

绘制具有多个属性的图形,类似于 Seaborn 中的“色调”

[转]CSS类选择器:一个元素同时使用多个类选择器