JTextFields 和 DocumentListeners
JTextFields and DocumentListeners
我想要两个文本字段(从现在开始是 A 和 B)共享与用户在其中任何一个上输入的内容相同的内容。我可以让一个镜像另一个(B镜像A)或相反(A镜像B)。但是当我保留两个 DocumentListeners 时,执行开始抛出异常。
根据 Oracle 的文档,我不能使用 DocumentListener 从 Listener 本身改变文档的内容。我觉得这很奇怪,因为我已经在第一种情况下(B 镜像 A)或相反的情况下做到了。无论如何,代码仍然 "works" 但每触发两个事件就会抛出一个异常。
对于这种特殊情况,KeyListeners 不可靠,我拒绝使用按钮,因为我喜欢 DocumentListener 提供的实时外观。
有什么建议吗?
这是我的代码:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Mirror {
private JTextField oriText;
private JTextField mirrorText;
private static int debugCounter; //Counts the times an Event is Triggered.
public static void main(String[] args) {
Mirror gui = new Mirror();
gui.build();
}
public void build(){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.gridx = 0;
c.gridy = 0;
JLabel original = new JLabel("Original");
panel.add(original, c);
c.gridy = 1;
oriText = new JTextField(10);
panel.add(oriText,c);
c.gridy = 2;
JLabel mirror = new JLabel("Mirror");
panel.add(mirror, c);
c.gridy = 3;
mirrorText = new JTextField(10);
panel.add(mirrorText, c);
mirrorText.getDocument().addDocumentListener(new MyDocumentListenerII()); // Comment this line to see only the 1st Case (B mirrors A)
oriText.getDocument().addDocumentListener(new MyDocumentListener()); // Comment this line to see only the 2nd Case (A mirrors B)
frame.pack();
frame.setVisible(true);
}
class MyDocumentListener implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
//Does nothing.
}
@Override
public void insertUpdate(DocumentEvent e) {
mirror();
}
@Override
public void removeUpdate(DocumentEvent e) {
mirror();
}
}
class MyDocumentListenerII implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
// Does nothing.
}
@Override
public void insertUpdate(DocumentEvent e) {
mirror1();
}
@Override
public void removeUpdate(DocumentEvent e) {
mirror1();
}
}
public void mirror(){
if (!oriText.getText().equals(mirrorText.getText())){ //Without this each Event trigger the other in some sort of Paradoxical cycle.
mirrorText.setText(oriText.getText());
debugCounter++;
System.out.println(debugCounter+" events triggered");
}
}
public void mirror1(){
if (!mirrorText.getText().equals(oriText.getText())){
oriText.setText(mirrorText.getText());
debugCounter++;
System.out.println(debugCounter+" events triggered");
}
}
}
我不完全确定这是 'correct' 的方法,但我所做的只是将相同的 Document
附加到每个 JTextField
.
这是我的初始化程序中的相关部分:
textFieldA = new JTextField();
textFieldA.setBounds(10, 11, 414, 20);
textFieldA.setColumns(10);
textFieldB = new JTextField();
textFieldB.setBounds(10, 42, 414, 20);
textFieldB.setColumns(10);
textFieldB.setDocument(textFieldA.getDocument());
现在 JTextField
都有相同的文档,所以它们不可能不同。
您遇到的问题是,由于两个 JTextField
都需要同步,所以每个字段的 DocumentListener
都需要更新另一个字段。但是,该更新导致另一个 DocumentListener
尝试更新第一个字段,导致抛出 IllegalStateException
,因为在执行 DocumentListener
时某些东西试图修改该字段。
解决方案是在为该字段执行 DocumentListener
时阻止对 setText(String)
的调用。这可以通过 boolean
变量来完成。下面是 DocumentListener
之一的代码:
textFieldA.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate (DocumentEvent e) {
blockA = true;
if (!blockB) textFieldB.setText(textFieldA.getText());
blockA = false;
}
@Override
public void insertUpdate (DocumentEvent e) {
blockA = true;
if (!blockB) textFieldB.setText(textFieldA.getText());
blockA = false;
}
@Override
public void changedUpdate (DocumentEvent e) {
blockA = true;
if (!blockB) textFieldB.setText(textFieldA.getText());
blockA = false;
}
});
其中 blockA
和 blockB
是 boolean
实例字段(或者可能是方法中的 final
变量)。现在,当方法在 textFieldA
的 DocumentListener
中触发时,会设置一个标志以指示不使用 textFieldA.setText(String)
。我们还看到了在设置 blockB
时如何阻止 textFieldB.setText(String)
。 textFieldB
的 DocumentListener
看起来差不多。
现在,textFieldA
不会在其 DocumentListener
内的调用期间设置,其他字段也是如此。无论如何,这样的调用都是多余的:此时每个字段的文本都是相同的。
我想要两个文本字段(从现在开始是 A 和 B)共享与用户在其中任何一个上输入的内容相同的内容。我可以让一个镜像另一个(B镜像A)或相反(A镜像B)。但是当我保留两个 DocumentListeners 时,执行开始抛出异常。
根据 Oracle 的文档,我不能使用 DocumentListener 从 Listener 本身改变文档的内容。我觉得这很奇怪,因为我已经在第一种情况下(B 镜像 A)或相反的情况下做到了。无论如何,代码仍然 "works" 但每触发两个事件就会抛出一个异常。
对于这种特殊情况,KeyListeners 不可靠,我拒绝使用按钮,因为我喜欢 DocumentListener 提供的实时外观。
有什么建议吗?
这是我的代码:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Mirror {
private JTextField oriText;
private JTextField mirrorText;
private static int debugCounter; //Counts the times an Event is Triggered.
public static void main(String[] args) {
Mirror gui = new Mirror();
gui.build();
}
public void build(){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.gridx = 0;
c.gridy = 0;
JLabel original = new JLabel("Original");
panel.add(original, c);
c.gridy = 1;
oriText = new JTextField(10);
panel.add(oriText,c);
c.gridy = 2;
JLabel mirror = new JLabel("Mirror");
panel.add(mirror, c);
c.gridy = 3;
mirrorText = new JTextField(10);
panel.add(mirrorText, c);
mirrorText.getDocument().addDocumentListener(new MyDocumentListenerII()); // Comment this line to see only the 1st Case (B mirrors A)
oriText.getDocument().addDocumentListener(new MyDocumentListener()); // Comment this line to see only the 2nd Case (A mirrors B)
frame.pack();
frame.setVisible(true);
}
class MyDocumentListener implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
//Does nothing.
}
@Override
public void insertUpdate(DocumentEvent e) {
mirror();
}
@Override
public void removeUpdate(DocumentEvent e) {
mirror();
}
}
class MyDocumentListenerII implements DocumentListener{
@Override
public void changedUpdate(DocumentEvent e) {
// Does nothing.
}
@Override
public void insertUpdate(DocumentEvent e) {
mirror1();
}
@Override
public void removeUpdate(DocumentEvent e) {
mirror1();
}
}
public void mirror(){
if (!oriText.getText().equals(mirrorText.getText())){ //Without this each Event trigger the other in some sort of Paradoxical cycle.
mirrorText.setText(oriText.getText());
debugCounter++;
System.out.println(debugCounter+" events triggered");
}
}
public void mirror1(){
if (!mirrorText.getText().equals(oriText.getText())){
oriText.setText(mirrorText.getText());
debugCounter++;
System.out.println(debugCounter+" events triggered");
}
}
}
我不完全确定这是 'correct' 的方法,但我所做的只是将相同的 Document
附加到每个 JTextField
.
这是我的初始化程序中的相关部分:
textFieldA = new JTextField();
textFieldA.setBounds(10, 11, 414, 20);
textFieldA.setColumns(10);
textFieldB = new JTextField();
textFieldB.setBounds(10, 42, 414, 20);
textFieldB.setColumns(10);
textFieldB.setDocument(textFieldA.getDocument());
现在 JTextField
都有相同的文档,所以它们不可能不同。
您遇到的问题是,由于两个 JTextField
都需要同步,所以每个字段的 DocumentListener
都需要更新另一个字段。但是,该更新导致另一个 DocumentListener
尝试更新第一个字段,导致抛出 IllegalStateException
,因为在执行 DocumentListener
时某些东西试图修改该字段。
解决方案是在为该字段执行 DocumentListener
时阻止对 setText(String)
的调用。这可以通过 boolean
变量来完成。下面是 DocumentListener
之一的代码:
textFieldA.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate (DocumentEvent e) {
blockA = true;
if (!blockB) textFieldB.setText(textFieldA.getText());
blockA = false;
}
@Override
public void insertUpdate (DocumentEvent e) {
blockA = true;
if (!blockB) textFieldB.setText(textFieldA.getText());
blockA = false;
}
@Override
public void changedUpdate (DocumentEvent e) {
blockA = true;
if (!blockB) textFieldB.setText(textFieldA.getText());
blockA = false;
}
});
其中 blockA
和 blockB
是 boolean
实例字段(或者可能是方法中的 final
变量)。现在,当方法在 textFieldA
的 DocumentListener
中触发时,会设置一个标志以指示不使用 textFieldA.setText(String)
。我们还看到了在设置 blockB
时如何阻止 textFieldB.setText(String)
。 textFieldB
的 DocumentListener
看起来差不多。
现在,textFieldA
不会在其 DocumentListener
内的调用期间设置,其他字段也是如此。无论如何,这样的调用都是多余的:此时每个字段的文本都是相同的。