2013-03-04 13 views
6

Questo è il mio codice:Perché JTextField.setText avvierà removeUpdate() di DocumentListener prima di changedUpdate()?

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

Quando clicco sul JButton per la prima volta, si chiamerà solo insertUpdate():

insertUpdate from txt1 
insertUpdate from txt2 

Ma se clicco di nuovo il pulsante, sarà chiamato removeUpdate() prima insertUpdate():

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

È questo il comportamento previsto o qualcosa di sbagliato nel mio codice?

Posso fare insertUpdate l'unico metodo che veniva chiamato quando si eseguiva JTextField.setText? Voglio assicurarmi che removeUpdate venga chiamato solo quando l'utente cancella il testo nel campo di testo. Come farlo?

risposta

5

Questo è il comportamento previsto per la sostituzione di stringhe. Che cosa fa in realtà setText() è rimuovere l'intera stringa e impostarne una nuova. Ecco l'implementazione di 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); 
    } 
} 

Come si può vedere, AbstractDocument.replace() viene eseguito per AbstractDocument docs. Altrimenti, vengono eseguiti remove() e insert().

Da AbstractDocument.replace() documentazione:

Elimina la regione di testo da compensazione per compensare + lunghezza e lo sostituisce con il testo. Spetta all'implementazione del modo in cui è implementato questo , alcune implementazioni potrebbero trattarlo come due distinte operazioni : una rimozione seguita da un inserto, altre potrebbero considerare lo sostituire come un'operazione atomica.

Quindi dipende dall'implementazione del documento. Ad esempio, PlainDocument eredita l'implementazione di base di AbstractDocument. A PlainDocument è il documento predefinito per i campi di testo.

È sempre possibile creare la propria implementazione del documento, se necessario, o magari l'installazione di un filtro di documenti. Vedi il tutorial Using Text Components per i dettagli. Non sono sicuro, però, qual è la ragione di questo cambiamento comportamentale che stai cercando di raggiungere.

+1

Il motivo è per la simulazione del collegamento JTextField perché setText() non è associato. Quando si tratta di mutuo vincolo, dovrebbe essere: il passaggio ad un'altra proprietà associata verrà propagato a JTextField.text; e la chiamata a JTextField.text deve essere propagata alla proprietà associata. Ma ciò che realmente accade ora è il passaggio a JTextField.text a volte si attivano 1 o 2 eventi di listener di documenti ed è difficile decidere quando interrompere l'associazione reciproca. – jocki