【问题标题】:Why JTextField.setText will fire DocumentListener's removeUpdate() before changedUpdate()?为什么 JTextField.setText 会在 changedUpdate() 之前触发 DocumentListener 的 removeUpdate()?
【发布时间】:2013-02-19 00:36:54
【问题描述】:

这是我的代码:

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Frame extends JFrame {

    private JTextField txt1 = new JTextField(10);
    private JTextField txt2 = new JTextField(10);
    private JButton btn = new JButton("Set Text");

    public Frame() {
        super("Latihan");
        setLayout(new FlowLayout());
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                txt1.setText("TEST"); txt2.setText("TEST2");
            }
        });

        txt1.getDocument().addDocumentListener(new TheDocumentListener("txt1"));
        txt2.getDocument().addDocumentListener(new TheDocumentListener("txt2"));

        add(txt1);
        add(txt2);
        add(btn);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    }

    public static void main (String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Frame();
            }
        });
    }
}

class TheDocumentListener implements DocumentListener {

    private String source;

    public TheDocumentListener(String source) {
        this.source = source;
    }
    @Override
    public void insertUpdate(DocumentEvent e) {
        System.out.println("insertUpdate from " + source);
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        System.out.println("removeUpdate from " + source);
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
        System.out.println("changedUpdate from " + source);
    }
}

当我第一次点击JButton时,只会调用insertUpdate()

insertUpdate from txt1
insertUpdate from txt2

但是如果我再次点击按钮,removeUpdate() 会在insertUpdate() 之前被调用:

removeUpdate from txt1
insertUpdate from txt1
removeUpdate from txt2
insertUpdate from txt2

这是预期的行为还是我的代码有问题?

我可以让insertUpdate 成为执行JTextField.setText 时调用的唯一方法吗?我想确保只有在用户删除文本字段中的文本时才调用removeUpdate。该怎么做?

【问题讨论】:

    标签: java swing jtextfield


    【解决方案1】:

    这是字符串替换的预期行为。 setText() 实际上所做的是删除整个字符串并设置一个新字符串。这里是JTextField.setText()的实现:

    public void setText(String t) {
        try {
            Document doc = getDocument();
            if (doc instanceof AbstractDocument) {
                ((AbstractDocument)doc).replace(0, doc.getLength(), t,null);
            }
            else {
                doc.remove(0, doc.getLength());
                doc.insertString(0, t, null);
            }
        } catch (BadLocationException e) {
        UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
        }
    }
    

    如您所见,AbstractDocument.replace() 是针对 AbstractDocument 文档执行的。否则,将执行 remove()insert()

    来自AbstractDocument.replace() 文档:

    删除从偏移到偏移+长度的文本区域,并且 用文本替换它。这取决于实施 已实现,某些实现可能会将其视为两个不同的 操作:删除后插入,其他人可能会处理 替换为一个原子操作。

    所以这取决于文档的实现。 PlainDocument 例如继承AbstractDocument 的基本实现。 PlainDocument 是文本字段的默认文档。

    如果需要,您可以随时创建自己的文档实现,或者安装文档过滤器。详见Using Text Components教程。但不确定,您试图实现的这种行为改变背后的原因是什么。

    【讨论】:

    • 原因是模拟JTextField绑定,因为setText()没有绑定。说到相互绑定,应该是:对其他绑定属性的改变会传播到JTextField.text;并且对 JTextField.text 的调用应该传播到绑定属性。但现在真正发生的是更改 JTextField.text 有时会触发 1 或 2 个文档侦听器事件,并且很难决定何时停止相互绑定。
    猜你喜欢
    • 2021-10-25
    • 2015-03-06
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    相关资源
    最近更新 更多