web-dev-qa-db-fra.com

Value Change Listener to JTextField

Je veux que la boîte de message apparaisse immédiatement après que l'utilisateur a changé la valeur dans le champ de texte. Actuellement, je dois appuyer sur la touche Entrée pour faire apparaître la boîte de message. Y a-t-il un problème avec mon code?

textField.addActionListener(new Java.awt.event.ActionListener() {
    public void actionPerformed(Java.awt.event.ActionEvent e) {

        if (Integer.parseInt(textField.getText())<=0){
            JOptionPane.showMessageDialog(null,
                    "Error: Please enter number bigger than 0", "Error Message",
                    JOptionPane.ERROR_MESSAGE);
        }       
    }
}

Toute aide serait appréciée!

203
user236501

Ajoutez un écouteur au document sous-jacent, qui est automatiquement créé pour vous.

// Listen for changes in the text
textField.getDocument().addDocumentListener(new DocumentListener() {
  public void changedUpdate(DocumentEvent e) {
    warn();
  }
  public void removeUpdate(DocumentEvent e) {
    warn();
  }
  public void insertUpdate(DocumentEvent e) {
    warn();
  }

  public void warn() {
     if (Integer.parseInt(textField.getText())<=0){
       JOptionPane.showMessageDialog(null,
          "Error: Please enter number bigger than 0", "Error Message",
          JOptionPane.ERROR_MESSAGE);
     }
  }
});
356
Codemwnci

La réponse habituelle à cette question est "utilisez un DocumentListener ". Cependant, je trouve toujours cette interface fastidieuse. Honnêtement, l'interface est trop sophistiquée. Il existe trois méthodes, pour l'insertion, la suppression et le remplacement de texte, lorsqu'une seule méthode est nécessaire: le remplacement. (Une insertion peut être considérée comme un remplacement de texte sans texte par un texte et une suppression peut être vue comme un remplacement de texte sans texte.)

En général, tout ce que vous voulez savoir, c’est lorsque le texte de la zone a été modifié . Par conséquent, une implémentation DocumentListener typique utilise les trois méthodes qui appellent une méthode.

Par conséquent, j’ai créé la méthode d’utilité suivante, qui vous permet d’utiliser un plus simple ChangeListener plutôt que DocumentListener. (Il utilise la syntaxe lambda de Java 8, mais vous pouvez l'adapter à l'ancien Java si nécessaire.)

/**
 * Installs a listener to receive notification when the text of any
 * {@code JTextComponent} is changed. Internally, it installs a
 * {@link DocumentListener} on the text component's {@link Document},
 * and a {@link PropertyChangeListener} on the text component to detect
 * if the {@code Document} itself is replaced.
 * 
 * @param text any text component, such as a {@link JTextField}
 *        or {@link JTextArea}
 * @param changeListener a listener to receieve {@link ChangeEvent}s
 *        when the text is changed; the source object for the events
 *        will be the text component
 * @throws NullPointerException if either parameter is null
 */
public static void addChangeListener(JTextComponent text, ChangeListener changeListener) {
    Objects.requireNonNull(text);
    Objects.requireNonNull(changeListener);
    DocumentListener dl = new DocumentListener() {
        private int lastChange = 0, lastNotifiedChange = 0;

        @Override
        public void insertUpdate(DocumentEvent e) {
            changedUpdate(e);
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            changedUpdate(e);
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            lastChange++;
            SwingUtilities.invokeLater(() -> {
                if (lastNotifiedChange != lastChange) {
                    lastNotifiedChange = lastChange;
                    changeListener.stateChanged(new ChangeEvent(text));
                }
            });
        }
    };
    text.addPropertyChangeListener("document", (PropertyChangeEvent e) -> {
        Document d1 = (Document)e.getOldValue();
        Document d2 = (Document)e.getNewValue();
        if (d1 != null) d1.removeDocumentListener(dl);
        if (d2 != null) d2.addDocumentListener(dl);
        dl.changedUpdate(null);
    });
    Document d = text.getDocument();
    if (d != null) d.addDocumentListener(dl);
}

Contrairement à l'ajout d'un écouteur directement au document, ceci gère le cas (peu commun) d'installation d'un nouvel objet document sur un composant texte. De plus, cela fonctionne autour du problème mentionné dans réponse de Jean-Marc Astesana , où le document déclenche parfois plus d'événements que nécessaire.

Quoi qu'il en soit, cette méthode vous permet de remplacer le code ennuyeux qui ressemble à ceci:

someTextBox.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) {
        doSomething();
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        doSomething();
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
        doSomething();
    }
});

Avec:

addChangeListener(someTextBox, e -> doSomething());

Code publié dans le domaine public. S'amuser!

44
Boann

Sachez que lorsque l'utilisateur modifie le champ, DocumentListener peut parfois recevoir deux événements. Par exemple, si l'utilisateur sélectionne tout le contenu du champ, puis appuyez sur une touche, vous recevrez un removeUpdate (tout le contenu est supprimé) et un insertUpdate. Dans votre cas, je ne pense pas que ce soit un problème, mais d’une manière générale, c’est le cas. Malheureusement, il semble qu'il soit impossible de suivre le contenu de textField sans sous-classer JTextField. Voici le code d'une classe qui fournit une propriété "text":

package net.yapbam.gui.widget;

import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

/** A JTextField with a property that maps its text.
 * <br>I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget.
 * <br>DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:<ol>
 * <li>One when the replaced text is removed.</li>
 * <li>One when the replacing text is inserted</li>
 * </ul>
 * The first event is ... simply absolutely misleading, it corresponds to a value that the text never had.
 * <br>Anoter problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException).
 * <br><br>Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval)
 * after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change.
 * <br><br>This widget guarantees that no "ghost" property change is thrown !
 * @author Jean-Marc Astesana
 * <BR>License : GPL v3
 */

public class CoolJTextField extends JTextField {
    private static final long serialVersionUID = 1L;

    public static final String TEXT_PROPERTY = "text";

    public CoolJTextField() {
        this(0);
    }

    public CoolJTextField(int nbColumns) {
        super("", nbColumns);
        this.setDocument(new MyDocument());
    }

    @SuppressWarnings("serial")
    private class MyDocument extends PlainDocument {
        private boolean ignoreEvents = false;

        @Override
        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            String oldValue = CoolJTextField.this.getText();
            this.ignoreEvents = true;
            super.replace(offset, length, text, attrs);
            this.ignoreEvents = false;
            String newValue = CoolJTextField.this.getText();
            if (!oldValue.equals(newValue)) CoolJTextField.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue);
        }

        @Override
        public void remove(int offs, int len) throws BadLocationException {
            String oldValue = CoolJTextField.this.getText();
            super.remove(offs, len);
            String newValue = CoolJTextField.this.getText();
            if (!ignoreEvents && !oldValue.equals(newValue)) CoolJTextField.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue);
        }
    }
15
Jean-Marc Astesana

Créez simplement une interface qui étend DocumentListener et implémente toutes les méthodes DocumentListener:

@FunctionalInterface
public interface SimpleDocumentListener extends DocumentListener {
    void update(DocumentEvent e);

    @Override
    default void insertUpdate(DocumentEvent e) {
        update(e);
    }
    @Override
    default void removeUpdate(DocumentEvent e) {
        update(e);
    }
    @Override
    default void changedUpdate(DocumentEvent e) {
        update(e);
    }
}

puis:

jTextField.getDocument().addDocumentListener(new SimpleDocumentListener() {
    @Override
    public void update(DocumentEvent e) {
        // Your code here
    }
});

ou vous pouvez même utiliser l'expression lambda:

jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
    // Your code here
});
12
Andrey Megvinov

Je sais que cela concerne un problème très ancien, mais cela m’a aussi posé quelques problèmes. Comme kleopatra a répondu dans un commentaire ci-dessus, j'ai résolu le problème avec un JFormattedTextField. Cependant, la solution nécessite un peu plus de travail, mais elle est plus simple.

La JFormattedTextField ne déclenche pas par défaut de modification de propriété après chaque modification de texte dans le champ. Le constructeur par défaut de JFormattedTextField ne crée pas de formateur.

Cependant, pour faire ce que l'OP a suggéré, vous devez utiliser un formateur qui invoquera la méthode commitEdit() après chaque édition valide du champ. La méthode commitEdit() est ce qui déclenche le changement de propriété par rapport à ce que je peux voir. Sans formateur, ceci est déclenché par défaut lors d'un changement de focus ou lorsque vous appuyez sur la touche Entrée.

Voir http://docs.Oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value pour plus de détails.

Créez un objet de formatage par défaut (DefaultFormatter) à transmettre au JFormattedTextField via son constructeur ou une méthode de définition. Une des méthodes du formateur par défaut est setCommitsOnValidEdit(boolean commit), qui permet au formateur de déclencher la méthode commitEdit() chaque fois que le texte est modifié. Ceci peut ensuite être capturé en utilisant une méthode PropertyChangeListener et la méthode propertyChange().

10
Astridax
textBoxName.getDocument().addDocumentListener(new DocumentListener() {
   @Override
   public void insertUpdate(DocumentEvent e) {
       onChange();
   }

   @Override
   public void removeUpdate(DocumentEvent e) {
      onChange();
   }

   @Override
   public void changedUpdate(DocumentEvent e) {
      onChange();
   } 
});

Mais je ne voudrais pas simplement analyser tout ce que l'utilisateur (peut-être par accident) touche sur son clavier dans un Integer. Vous devriez attraper tous les Exceptions jetés et vous assurer que le JTextField n'est pas vide.

1
DerBobby

c'était la version mise à jour de Codemwnci. son code est assez bon et fonctionne très bien sauf le message d'erreur. Pour éviter les erreurs, vous devez modifier l'instruction de condition.

  // Listen for changes in the text
textField.getDocument().addDocumentListener(new DocumentListener() {
  public void changedUpdate(DocumentEvent e) {
    warn();
  }
  public void removeUpdate(DocumentEvent e) {
    warn();
  }
  public void insertUpdate(DocumentEvent e) {
    warn();
  }

  public void warn() {
     if (textField.getText().length()>0){
       JOptionPane.showMessageDialog(null,
          "Error: Please enter number bigger than 0", "Error Massage",
          JOptionPane.ERROR_MESSAGE);
     }
  }
});

Vous pouvez même utiliser "MouseExited" pour contrôler. exemple:

 private void jtSoMauMouseExited(Java.awt.event.MouseEvent evt) {                                    
        // TODO add your handling code here:
        try {
            if (Integer.parseInt(jtSoMau.getText()) > 1) {
                //auto update field
                SoMau = Integer.parseInt(jtSoMau.getText());
                int result = SoMau / 5;

                jtSoBlockQuan.setText(String.valueOf(result));
            }
        } catch (Exception e) {

        }

    }   
1
fishgold192

Utilisez un KeyListener (qui déclenche sur n'importe quelle clé) plutôt que le ActionListener (qui déclenche à l'entrée)

0
nick

Je suis nouveau dans WindowBuilder et, en fait, je reviens à Java après quelques années, mais j’ai implémenté "quelque chose", puis j’ai pensé que j’y retournerais pour trouver ce fil.

Je suis en train de tester cela, alors, basé sur le fait d'être nouveau dans tout cela, je suis sûr que je dois manquer quelque chose.

Voici ce que j'ai fait, où "runTxt" est une zone de texte et "runName" est un membre de données de la classe:

public void focusGained(FocusEvent e)
    {
    if (e.getSource() == runTxt)
        {
        System.out.println("runTxt got focus");
        runTxt.selectAll();
        }
    }
public void focusLost(FocusEvent e)
    {
    if (e.getSource() == runTxt)
        {
        System.out.println("runTxt lost focus");
        if(!runTxt.getText().equals(runName))runName= runTxt.getText();
        System.out.println("runText.getText()= " + runTxt.getText() + "; runName= " + runName);
        }
    }

Cela semble beaucoup plus simple que ce qui existe ici jusqu'à présent, et semble fonctionner, mais, comme je suis en train d'écrire ceci, j'aimerais bien entendre parler de toutes les pièges négligés. Est-ce un problème que l'utilisateur puisse entrer et quitter la zone de texte sans apporter de modification? Je pense que tout ce que vous avez fait est une tâche inutile.

0
RocketMan

Si nous utilisons la méthode exécutable, SwingUtilities.invokeLater () lors de l'utilisation de l'application d'écoute de document se coince parfois et prend du temps pour mettre à jour le résultat (selon mon expérience). Au lieu de cela, nous pouvons également utiliser l'événement KeyReleased pour l'écouteur de modification de champ de texte, comme indiqué ici .

usernameTextField.addKeyListener(new KeyAdapter() {
        public void keyReleased(KeyEvent e) {
            JTextField textField = (JTextField) e.getSource();
            String text = textField.getText();
            textField.setText(text.toUpperCase());
        }
    });
0