webentwicklung-frage-antwort-db.com.de

Value Change Listener zu JTextField

Ich möchte, dass das Meldungsfeld sofort angezeigt wird, nachdem der Benutzer den Wert im Textfeld geändert hat. Derzeit muss ich die Eingabetaste drücken, damit das Meldungsfeld angezeigt wird. Stimmt etwas mit meinem Code nicht?

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

Jede Hilfe wäre dankbar!

203
user236501

Fügen Sie dem zugrunde liegenden Dokument einen Listener hinzu, der automatisch für Sie erstellt wird.

// 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

Die übliche Antwort darauf lautet "use a DocumentListener ". Allerdings finde ich diese Schnittstelle immer umständlich. Ehrlich gesagt ist die Schnittstelle überarbeitet. Es gibt drei Methoden zum Einfügen, Entfernen und Ersetzen von Text, wenn nur eine Methode erforderlich ist: Ersetzen. (Ein Einfügen kann als Ersatz für keinen Text durch einen Text und ein Entfernen als Ersatz für einen Text ohne Text angesehen werden.)

Normalerweise müssen Sie nur wissen , wann sich der Text in der Box geändert hat , sodass eine typische DocumentListener -Implementierung die drei Methoden aufruft eine Methode.

Daher habe ich die folgende Utility-Methode erstellt, mit der Sie ein einfacheres ChangeListener anstelle eines DocumentListener verwenden können. (Es wird die Lambda-Syntax von Java 8 verwendet, aber Sie können sie bei Bedarf für alte Java anpassen.)

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

Im Gegensatz zum Hinzufügen eines Listeners direkt zum Dokument wird hiermit der (seltene) Fall behandelt, dass Sie ein neues Dokumentobjekt auf einer Textkomponente installieren. Darüber hinaus wird das in Antwort von Jean-Marc Astesana erwähnte Problem umgangen, bei dem das Dokument manchmal mehr Ereignisse auslöst, als erforderlich sind.

Auf jeden Fall können Sie mit dieser Methode lästigen Code ersetzen, der so aussieht:

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

Mit:

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

Code für Public Domain freigegeben. Habe Spaß!

44
Boann

Beachten Sie, dass der DocumentListener manchmal zwei Ereignisse empfangen kann, wenn der Benutzer das Feld ändert. Wenn der Benutzer beispielsweise den gesamten Feldinhalt auswählt und dann eine Taste drückt, erhalten Sie ein removeUpdate (der gesamte Inhalt ist remove) und ein insertUpdate. In Ihrem Fall glaube ich nicht, dass es ein Problem ist, aber im Allgemeinen ist es das. Leider scheint es keine Möglichkeit zu geben, den Inhalt von textField zu verfolgen, ohne JTextField als Unterklasse zu definieren. Hier ist der Code einer Klasse, die eine "text" -Eigenschaft bereitstellt:

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

Erstellen Sie einfach eine Schnittstelle, die DocumentListener erweitert und alle DocumentListener-Methoden implementiert:

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

und dann:

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

oder Sie können sogar Lambda-Ausdruck verwenden:

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

Ich weiß, dass dies ein wirklich altes Problem ist, aber es hat mir auch einige Probleme bereitet. Als kleopatra in einem Kommentar oben geantwortet hat, habe ich das Problem mit einem JFormattedTextField gelöst. Die Lösung erfordert jedoch etwas mehr Arbeit, ist aber ordentlicher.

Das JFormattedTextField löst standardmäßig keine Eigenschaftsänderung nach jeder Textänderung im Feld aus. Der Standardkonstruktor von JFormattedTextField erstellt keinen Formatierer.

Um jedoch das zu tun, was das OP vorschlug, müssen Sie einen Formatierer verwenden, der nach jeder gültigen Bearbeitung des Feldes die Methode commitEdit() aufruft. Die commitEdit() -Methode löst die Eigenschaftsänderung von dem aus, was ich sehen kann, und ohne den Formatierer wird dies standardmäßig bei einer Fokusänderung oder beim Drücken der Eingabetaste ausgelöst.

Weitere Informationen finden Sie unter http://docs.Oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value .

Erstellen Sie ein Standardformatierungsobjekt (DefaultFormatter), das über seinen Konstruktor oder eine Setter-Methode an JFormattedTextField übergeben werden soll. Eine Methode des Standardformatierers ist setCommitsOnValidEdit(boolean commit), mit der der Formatierer festgelegt wird, dass die commitEdit() -Methode bei jeder Änderung des Texts ausgelöst wird. Dies kann dann mit einer PropertyChangeListener und der propertyChange() Methode abgeholt werden.

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

Aber ich würde nicht einfach alles analysieren, was der Benutzer (vielleicht aus Versehen) auf seiner Tastatur in ein Integer tippt. Sie sollten alle geworfenen Exceptions fangen und sicherstellen, dass das JTextField nicht leer ist.

1
DerBobby

es war die Update-Version von Codemwnci. Sein Code ist in Ordnung und funktioniert bis auf die Fehlermeldung hervorragend. Um Fehler zu vermeiden, müssen Sie die Bedingungsanweisung ändern.

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

Sie können sogar "MouseExited" zur Steuerung verwenden. Beispiel:

 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

Verwenden Sie einen KeyListener (der bei jedem Schlüssel ausgelöst wird) anstelle des ActionListener (der bei Eingabe ausgelöst wird)

0
nick

Ich bin brandneu in WindowBuilder und komme tatsächlich erst nach ein paar Jahren wieder auf Java), aber ich habe "etwas" implementiert, dann dachte ich, ich würde es nachschlagen und stieß auf dies Faden.

Ich bin gerade dabei, das zu testen, also bin ich mir sicher, dass ich etwas vermissen muss, da ich in all dem neu bin.

Folgendes habe ich getan, wobei "runTxt" ein Textfeld und "runName" ein Datenelement der Klasse ist:

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

Scheint viel einfacher als das, was bisher hier war, und scheint zu funktionieren, aber da ich gerade dabei bin, dies zu schreiben, würde ich es begrüßen, von übersehenen Fallstricken zu hören. Ist es ein Problem, dass der Benutzer das Textfeld betreten und verlassen kann, ohne eine Änderung vorzunehmen? Ich denke, alles, was Sie getan haben, ist eine unnötige Aufgabe.

0
RocketMan

Wenn wir die ausführbare Methode SwingUtilities.invokeLater () verwenden, bleibt die Anwendung Document Listener manchmal hängen und benötigt Zeit, um das Ergebnis zu aktualisieren (gemäß meinem Experiment). Stattdessen können wir auch das KeyReleased-Ereignis für den Textfeldänderungs-Listener verwenden, wie erwähnt hier .

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