Zmiana słuchacza wartości na JTextField

Chcę, aby okno wiadomości pojawiło się natychmiast po zmianie wartości przez użytkownika w polu tekstowym. Obecnie muszę nacisnąć klawisz enter, aby pole wiadomości wyskoczyło. Czy coś jest nie tak z moim kodem?

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);
        }       
    }
}
Każda pomoc będzie mile widziana!
Author: kc2001, 2010-10-17

11 answers

Dodaj słuchacz do bazowego dokumentu, który jest automatycznie tworzony dla Ciebie.

// 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 Massage",
          JOptionPane.ERROR_MESSAGE);
     }
  }
});
 332
Author: Codemwnci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-02 07:06:26

Zwyczajową odpowiedzią na to jest " użyj DocumentListener". Jednak zawsze uważam ten interfejs za kłopotliwy. Szczerze mówiąc interfejs jest przerobiony. Ma trzy metody wstawiania, usuwania i zastępowania tekstu, gdy potrzebuje tylko jednej metody: zastępowania. (Wstawianie może być postrzegane jako zastąpienie tekstu bez tekstu, a usunięcie może być postrzegane jako zastąpienie tekstu bez tekstu.)

Zazwyczaj wszystko, co chcesz wiedzieć, to gdy tekst w polu ma zmieniono , więc typowa implementacja DocumentListener posiada trzy metody wywołujące jedną metodę.

Dlatego zrobiłem następującą metodę użytkową, która pozwala na użycie prostszego ChangeListener zamiast DocumentListener. (Używa składni lambda Javy 8, ale w razie potrzeby można ją dostosować do starej Javy.)

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

W przeciwieństwie do dodawania detektora bezpośrednio do dokumentu, obsługuje to (nietypowy) przypadek instalacji nowego obiektu dokumentu na komponencie tekstowym. Dodatkowo działa wokół problem wspomniany w odpowiedzi Jeana-Marca Astesany , gdzie dokument czasami uruchamia więcej zdarzeń, niż musi.

W każdym razie ta metoda pozwala zastąpić irytujący kod, który wygląda tak:

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

Z:

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

Kod wydany do domeny publicznej. Baw się dobrze!

 38
Author: Boann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-24 19:51:10

Należy pamiętać, że gdy użytkownik zmodyfikuje pole, DocumentListener może czasami otrzymać dwa zdarzenia. Na przykład, jeśli użytkownik wybierze całą zawartość pola, a następnie naciśnie klawisz, otrzymasz removeUpdate (cała zawartość jest usunięta) i insertUpdate. W Twoim przypadku, nie sądzę, że jest to problem, ale ogólnie rzecz biorąc, jest. Niestety, wydaje się, że nie ma sposobu na śledzenie zawartości pola tekstowego bez podklasowania JTextField. Oto Kod klasy, która dostarcza " tekst" właściwość:

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);
        }
    }
 14
Author: Jean-Marc Astesana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-02 13:23:02

Wiem, że odnosi się to do naprawdę starego problemu, jednak sprawiło mi to również pewne problemy. Jak odpowiedział kleopatra w komentarzu powyżej, rozwiązałem problem JFormattedTextField. Rozwiązanie wymaga jednak nieco więcej pracy, ale jest bardziej schludne.

JFormattedTextField domyślnie nie powoduje zmiany właściwości po każdej zmianie tekstu w polu. Domyślny konstruktor JFormattedTextField nie tworzy formatera.

Jednak, aby zrobić to, co sugerował OP, musisz użyć formatera która wywoła metodę commitEdit() po każdej poprawnej edycji pola. Metoda commitEdit() jest tym, co powoduje zmianę właściwości z tego, co widzę i bez formatera, jest to domyślnie uruchamiane przy zmianie ostrości lub po naciśnięciu klawisza enter.

Zobacz http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value Po Więcej Szczegółów.

Tworzy domyślny obiekt formatujący (DefaultFormatter), który ma zostać przekazany JFormattedTextField poprzez jego konstruktor albo metodą setera. Jedną z metod domyślnego formatowania jest setCommitsOnValidEdit(boolean commit), która ustawia formatowanie, aby wyzwalało metodę commitEdit() za każdym razem, gdy tekst jest zmieniany. Można to następnie odczytać za pomocą metody PropertyChangeListener i propertyChange().

 10
Author: Astridax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:36

Wystarczy utworzyć interfejs, który rozszerza DocumentListener i implementuje wszystkie metody 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);
    }
}

A następnie:

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

Lub możesz nawet użyć wyrażenia lambda:

jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
    // Your code here
});
 7
Author: Andrey Megvinov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-02 17:44:58

Możesz użyć nawet "MouseExited" do sterowania. przykład:

 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
Author: fishgold192,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-21 02:30:08

Była to aktualizacja Codemwnci. jego kod jest całkiem w porządku i działa świetnie, z wyjątkiem Komunikatu o błędzie. Aby uniknąć błędu, należy zmienić instrukcję 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);
     }
  }
});
 1
Author: Bipul Chandra Dev Nath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-02 08:24:17
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();
   } 
});

Ale nie przetworzyłbym niczego, co użytkownik (może przypadkowo) dotknie klawiatury w Integer. Należy złapać wszelkie ExceptionS rzucone i upewnić się, że JTextField nie jest pusty.

 1
Author: DerBobby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-15 08:41:07

Użyj KeyListener (który uruchamia się na dowolnym klawiszu) zamiast ActionListener (który uruchamia się na enter)

 0
Author: nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-04 02:03:25

Jestem zupełnie nowy w Windowbuilderze, a właściwie dopiero wracam do Javy po kilku latach, ale zaimplementowałem "coś", potem pomyślałem, że poszukam i natknąłem się na ten wątek.

Jestem w trakcie testowania tego, więc bazując na tym, że jestem nowy w tym wszystkim, jestem pewien, że coś mi umyka.

Oto co zrobiłem, gdzie "runTxt" to pole tekstowe, a "runName"to członek danych klasy:

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

Wydaje się dużo prostsze niż to, co jest tutaj do tej pory, i wydaje się być pracuję, ale skoro jestem w trakcie pisania tego, byłbym wdzięczny za wysłuchanie każdego pomijanego Gotcha. Czy jest to problem, że użytkownik może wejść i zostawić pole tekstowe bez dokonywania zmian? Myślę, że wszystko, co zrobiłeś, to niepotrzebne zadanie.

 0
Author: RocketMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-27 01:45:10

DocumentFilter ? Daje Ci możliwość manipulowania.

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

Przepraszam. J am using Jython (Python in Java) - but easy to understand
# 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));
 -1
Author: papuga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 08:33:37