2013-03-04 5 views
6

Dies ist mein Code:Warum JTextField.setText DocumentUistener die removeUpdate() vor ÄnderungUpdate() ausgelöst wird?

insertUpdate from txt1 
insertUpdate from txt2 

Aber wenn ich die Taste erneut klicken, wird removeUpdate() aufgerufen werden:

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); 
    } 
} 

Als ich zum ersten Mal auf der JButton klicken, wird nur insertUpdate() wird aufgerufen, vor insertUpdate():

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

Ist das erwartete Verhalten oder etwas falsch in meinem Code?

Kann ich insertUpdate die einzige Methode machen, die beim Ausführen von JTextField.setText aufgerufen wurde? Ich möchte sicherstellen, removeUpdate wird nur aufgerufen, wenn Benutzer Text im Textfeld löschen. Wie geht das?

Antwort

5

Dies ist das erwartete Verhalten beim Ersetzen von Zeichenfolgen. Was setText() tatsächlich tut, ist die gesamte Zeichenfolge zu entfernen und eine neue zu setzen. Hier ist die Implementierung von 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); 
    } 
} 

Wie Sie sehen können, AbstractDocument.replace() für AbstractDocument docs ausgeführt. Andernfalls werden remove() und insert() ausgeführt.

Von AbstractDocument.replace() Dokumentation:

Löscht den Textbereich von Offset + Länge zu kompensieren, und ersetzt es mit Text. Es liegt an der Implementierung, wie diese implementiert wird, einige Implementierungen können dies als zwei verschiedene Operationen behandeln: eine Entfernung gefolgt von einer Einfügung, andere können behandeln die ersetzen als eine atomare Operation.

Es hängt also von der Dokumentenimplementierung ab. PlainDocument zum Beispiel erbt die grundlegende Implementierung von AbstractDocument. A PlainDocument ist das Standarddokument für Textfelder.

Sie können jederzeit eine eigene Dokumentimplementierung erstellen oder einen Dokumentfilter installieren. Einzelheiten finden Sie unter Using Text Components Tutorial. Nicht sicher, was ist der Grund für diese Verhaltensänderung, die Sie erreichen wollen?

+1

Der Grund ist für die Simulation der JTextField-Bindung, da setText() nicht gebunden ist. Wenn es um gegenseitige Bindung geht, sollte es sein: Wechsel zu anderen gebundenen Eigenschaften wird zu JTextField.text propagiert; und der Aufruf von JTextField.text sollte an die gebundene Eigenschaft weitergegeben werden. Aber was jetzt wirklich passiert, ist der Wechsel zu JTextField.text wird manchmal 1 oder 2 Dokument Listener Ereignisse auslösen und es ist schwer zu entscheiden, wann die gegenseitige Bindung zu stoppen ist. – jocki

Verwandte Themen