Wert Änderungslistener für JTextField


215

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!

Antworten:


373

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

gutes Format für die Warnung / Typbesetzung. Das gleiche Muster ist nützlich für den Umgang mit doppelten Beträgen (eingegebene oder angezeigte Verkaufszahlen / Preise)
Max West

Es funktioniert gut, aber ich habe eine Abfrage, dass ich eine Methode aufrufen möchte, wenn ich Text in ein Textfeld einfüge. Ich habe nicht viel Ahnung, wie es gemacht wird ..

Ich hatte Probleme mit einer JTable, die beim Klicken auf eine andere Tabellenzelle keine Textfeldaktualisierungen von einer bearbeitbaren JComboBox erhielt, und die Funktion insertUpdate hier war die einzige Möglichkeit, damit sie ordnungsgemäß funktioniert.
Winchella


51

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

Normalerweise möchten Sie nur wissen, wann sich der Text in der Box geändert hat. In einer typischen DocumentListenerImplementierung werden also drei Methoden verwendet, die eine Methode aufrufen.

Aus diesem Grund habe ich die folgende Dienstprogrammmethode erstellt, mit der Sie eine einfachere ChangeListenerals eine verwenden können DocumentListener. (Es verwendet die Lambda-Syntax von Java 8, kann jedoch bei Bedarf an altes Java angepasst werden.)

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

Anders als beim direkten Hinzufügen eines Listeners zum Dokument wird hiermit der (ungewöhnliche) Fall behandelt, dass Sie ein neues Dokumentobjekt auf einer Textkomponente installieren. Darüber hinaus wird das in der Antwort von Jean-Marc Astesana erwähnte Problem umgangen , bei dem das Dokument manchmal mehr Ereignisse auslöst , als es benötigt.

Auf jeden Fall können Sie mit dieser Methode lästigen Code ersetzen, der folgendermaßen 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 die Öffentlichkeit freigegeben. Habe Spaß!


5
Ähnliche Lösung: Erstellen Sie eine abstract class DocumentChangeListener implements DocumentListenermit einer zusätzlichen abstrakten Methode change(DocumentEvent e), die Sie von allen drei anderen Methoden aufrufen. Scheint mir offensichtlicher, da es mehr oder weniger die gleiche Logik wie die abstract *AdapterZuhörer verwendet.
Geronimo

+1 als changedUpdateMethode wird explizit durch einen Aufruf in jedem von insertUpdateund aufgerufen removeUpdate, damit es funktioniert.
Kais

16

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 den Lambda-Ausdruck verwenden:

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

1
Vergessen Sie nicht, dass diese Lösung in allen Versionen vor Java 8 eine abstrakte Klasse anstelle einer Schnittstelle erfordert.
klaar

15

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 wird entfernt) und ein insertUpdate. In Ihrem Fall denke ich nicht, dass es ein Problem ist, aber im Allgemeinen ist es das. Leider scheint es keine Möglichkeit zu geben, den Inhalt des textField zu verfolgen, ohne JTextField zu unterklassifizieren. 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);
        }
    }

3
Schwingen bereits hat eine Art von Textfield , das Dokument Änderungen an einer Eigenschaft abbildet - es heißt JFormattedTextField :-)
kleopatra

11

Ich weiß, dass dies ein wirklich altes Problem ist, aber es hat mir auch einige Probleme verursacht. Als Kleopatra oben in einem Kommentar antwortete, löste ich das Problem mit a JFormattedTextField. Die Lösung erfordert jedoch etwas mehr Arbeit, ist aber ordentlicher.

Das JFormattedTextFieldlöst standardmäßig keine Eigenschaftsänderung aus, nachdem sich jeder Text im Feld geändert hat. Der Standardkonstruktor von erstellt JFormattedTextFieldkeinen Formatierer.

Um jedoch das zu tun, was das OP vorgeschlagen hat, müssen Sie einen Formatierer verwenden, der die commitEdit()Methode nach jeder gültigen Bearbeitung des Felds 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 Standardobjekt formatter ( DefaultFormatter), das JFormattedTextFieldentweder über seinen Konstruktor oder eine Setter-Methode an das Objekt übergeben werden soll . Eine Methode des Standardformatierers ist setCommitsOnValidEdit(boolean commit)das Festlegen des Formatierers zum Auslösen descommitEdit() Methode jedes Mal , wenn der Text geändert wird. Dies kann dann mit a PropertyChangeListenerund der propertyChange()Methode aufgenommen werden.


2
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 (möglicherweise aus Versehen) auf seiner Tastatur berührt Integer. Sie sollten alle Exceptiongeworfenen s fangen und sicherstellen, dass die JTextFieldnicht leer sind.


2

Wenn wir die ausführbare Methode SwingUtilities.invokeLater () verwenden, während die Document Listener-Anwendung verwendet wird, bleibt sie manchmal hängen und benötigt Zeit, um das Ergebnis zu aktualisieren (gemäß meinem Experiment). Stattdessen können wir das KeyReleased-Ereignis auch für den hier erwähnten Listener zum Ändern von Textfeldern verwenden .

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

1

Es war die Update-Version von Codemwnci. Sein Code ist ganz 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);
     }
  }
});

Ihre Anpassung löst den Fehlermeldungsdialog aus, wenn eine Zeichenfolge länger als Länge = 0 in das Textfeld eingegeben wird. Das ist also im Grunde jede andere Zeichenfolge als eine leere Zeichenfolge. Das ist nicht die gewünschte Lösung.
Klaar

0

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) {

        }

    }   

6
nicht wirklich: die Anforderung ist etwas zu tun, wenn der Text geändert wird - das hat nichts mit mouseEvents zu tun ;-)
kleopatra

0

Ich bin brandneu in WindowBuilder und komme nach ein paar Jahren wieder zu Java, aber ich habe "etwas" implementiert, dann dachte ich, ich würde es nachschlagen und bin auf diesen Thread gestoßen.

Ich bin gerade dabei, dies zu testen. Da ich neu in all dem bin, bin ich mir sicher, dass mir etwas fehlen muss.

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 zu sein als das, was bisher hier ist, 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.


-1

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


Dies funktioniert nicht, da der Wert des Felds nicht richtig erfasst wird und field.getText()der Anfangswert zurückgegeben wird. und das Ereignis ( arg0.getKeyChar()) gibt die Taste zurück. Eine Fehlerprüfung ist erforderlich, um festzustellen, ob Sie mit Feldtext verketten sollten.
Glend

@glend können Sie das Ereignis keyReleased anstelle des Ereignisses keyTyped verwenden. Es hat bei mir funktioniert und den vollen Wert bekommen.
Kakumanu siva krishna 1.

-1

DocumentFilter ? Es gibt Ihnen die Möglichkeit zu manipulieren.

[ http://www.java2s.com/Tutorial/Java/0240__Swing/FormatJTextFieldstexttouppercase.htm ]

Es tut uns leid. Ich benutze Jython (Python in Java) - aber leicht zu verstehen

# python style
# upper chars [ text.upper() ]

class myComboBoxEditorDocumentFilter( DocumentFilter ):
def __init__(self,jtext):
    self._jtext = jtext

def insertString(self,FilterBypass_fb, offset, text, AttributeSet_attrs):
    txt = self._jtext.getText()
    print('DocumentFilter-insertString:',offset,text,'old:',txt)
    FilterBypass_fb.insertString(offset, text.upper(), AttributeSet_attrs)

def replace(self,FilterBypass_fb, offset, length, text, AttributeSet_attrs):
    txt = self._jtext.getText()
    print('DocumentFilter-replace:',offset, length, text,'old:',txt)
    FilterBypass_fb.replace(offset, length, text.upper(), AttributeSet_attrs)

def remove(self,FilterBypass_fb, offset, length):
    txt = self._jtext.getText()
    print('DocumentFilter-remove:',offset, length, 'old:',txt)
    FilterBypass_fb.remove(offset, length)

// (java style ~example for ComboBox-jTextField)
cb = new ComboBox();
cb.setEditable( true );
cbEditor = cb.getEditor();
cbEditorComp = cbEditor.getEditorComponent();
cbEditorComp.getDocument().setDocumentFilter(new myComboBoxEditorDocumentFilter(cbEditorComp));
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.