This is my code:
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);
}
}
When I click on the JButton for the first time, only insertUpdate()
will be called:
insertUpdate from txt1
insertUpdate from txt2
But if I click the button again, removeUpdate()
will be called before insertUpdate()
:
removeUpdate from txt1
insertUpdate from txt1
removeUpdate from txt2
insertUpdate from txt2
Is this expected behaviour or something wrong in my code?
Can I make insertUpdate
the only method that was being called when performing JTextField.setText
? I want to make sure removeUpdate
is being called only when user delete text in the text field. How to do that?
This is the expected behavior of string replacement. What setText()
actually does is remove the whole string and set a new one. Here is the implementation of 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);
}
}
As you can see, AbstractDocument.replace()
is executed for AbstractDocument
docs. Otherwise, remove()
and insert()
are executed.
From AbstractDocument.replace() documentation:
Deletes the region of text from offset to offset + length, and replaces it with text. It is up to the implementation as to how this is implemented, some implementations may treat this as two distinct operations: a remove followed by an insert, others may treat the replace as one atomic operation.
So it depends on the document implementation. PlainDocument
for example inherits basic implementation of AbstractDocument
. A PlainDocument
is the default document for text fields.
You can always create you own document implementation if needed, or maybe installing a document filter. See Using Text Components tutorial for details. Not sure though, what is the reason behind this behavior change you're trying to achieve.