2013-03-04 9 views
6

To jest mój kod:Dlaczego JTextField.setText uruchomi removeUpdate() DocumentListener przed changeUpdate()?

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

Kiedy klikam na JButton po raz pierwszy, tylko insertUpdate() będzie nazwane:

insertUpdate from txt1 
insertUpdate from txt2 

Ale gdybym ponownie kliknij przycisk, removeUpdate() zostanie wywołana przed insertUpdate():

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

jest to oczekiwane zachowanie lub coś złego w moim kodu?

Czy mogę wykonać insertUpdate jedyną metodę, która została wywołana podczas wykonywania JTextField.setText? Chcę się upewnić, że wywoływana jest removeUpdate tylko wtedy, gdy użytkownik usunie tekst w polu tekstowym. Jak to zrobić?

Odpowiedz

5

Jest to oczekiwane zachowanie zamiany ciągów znaków. To, co faktycznie robi setText(), usuwa cały ciąg i ustawia nowy. Oto realizacja 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); 
    } 
} 

Jak widać, AbstractDocument.replace() jest wykonywany za AbstractDocument docs. W przeciwnym razie zostaną wykonane remove() i insert().

Od AbstractDocument.replace() dokumentacji:

Usuwa obszar tekstu z offset przesunięcie + długość i zastępuje go z tekstem. To zależy od implementacji tego , niektóre implementacje mogą traktować to jako dwie odrębne operacje: usunięcie, a następnie wstawienie, inne mogą traktować zastąpienie jako jedną operację atomową.

Wszystko zależy od implementacji dokumentu. Na przykład PlainDocument dziedziczy podstawową implementację AbstractDocument. A PlainDocument jest domyślnym dokumentem dla pól tekstowych.

Zawsze możesz utworzyć własną implementację dokumentu, jeśli to konieczne, lub może zainstalować filtr dokumentu. Szczegółowe informacje znajdują się w samouczku Using Text Components. Nie wiesz jednak, jaki jest powód zmiany zachowania, którą próbujesz osiągnąć.

+1

Powodem jest symulacja wiązania JTextField, ponieważ metoda setText() nie jest powiązana. Jeśli chodzi o wzajemne powiązanie, powinno być: zmiana na inną powiązaną właściwość będzie propagowana do JTextField.text; i wywołanie JTextField.text powinno być propagowane do właściwości bound. Ale to, co naprawdę się stało, to zmiana na JTextField.text czasami wyzwala 1 lub 2 zdarzenia detektora dokumentów i ciężko jest zdecydować, kiedy przerwać wzajemne wiązanie. – jocki

Powiązane problemy