Слушатель изменения значения на JTextField - PullRequest
202 голосов
/ 17 октября 2010

Я хочу, чтобы окно сообщения появилось сразу после того, как пользователь изменит значение в текстовом поле.В настоящее время мне нужно нажать клавишу ввода, чтобы выскочило окно сообщения.Что-то не так с моим кодом?

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

Любая помощь будет признательна!

Ответы [ 12 ]

355 голосов
/ 17 октября 2010

Добавьте слушателя в базовый документ, который автоматически создается для вас.

// 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);
     }
  }
});
44 голосов
/ 28 ноября 2014

Обычный ответ на это «использовать DocumentListener». Тем не менее, я всегда нахожу этот интерфейс громоздким. По правде говоря, интерфейс перегружен. Он имеет три метода для вставки, удаления и замены текста, когда требуется только один метод: замена. (Вставка может рассматриваться как замена текста без текста, а удаление может рассматриваться как замена текста без текста.)

Обычно все, что вам нужно знать, это , когда текст в поле изменился , поэтому типичная реализация DocumentListener имеет три метода, вызывающих один метод.

Поэтому я сделал следующий служебный метод, который позволяет вам использовать более простой ChangeListener вместо DocumentListener. (Он использует лямбда-синтаксис Java 8, но при необходимости вы можете адаптировать его для старой Java.)

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

В отличие от добавления слушателя непосредственно в документ, это обрабатывает (необычный) случай, когда вы устанавливаете новый объект документа в текстовом компоненте. Кроме того, он работает вокруг проблемы, упомянутой в ответ Жана-Марка Астесаны , где документ иногда запускает больше событий, чем нужно.

В любом случае, этот метод позволяет заменить раздражающий код, который выглядит следующим образом:

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

С:

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

Код опубликован в открытом доступе. Веселись!

15 голосов
/ 02 января 2012

Имейте в виду, что когда пользователь изменяет поле, DocumentListener может иногда получать два события.Например, если пользователь выбирает содержимое всего поля, а затем нажимает клавишу, вы получите removeUpdate (все содержимое удаляется) и insertUpdate.В вашем случае, я не думаю, что это проблема, но, вообще говоря, это так.К сожалению, кажется, что нет способа отследить содержимое textField без подкласса JTextField.Вот код класса, который предоставляет свойство "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);
        }
    }
10 голосов
/ 08 сентября 2016

Просто создайте интерфейс, который расширяет DocumentListener и реализует все методы 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);
    }
}

, а затем:

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

или вы даже можете использовать лямбда-выражение:

jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
    // Your code here
});
10 голосов
/ 22 мая 2013

Я знаю, что это относится к действительно старой проблеме, однако, это также вызвало некоторые проблемы. Когда kleopatra ответил в комментарии выше, я решил проблему с JFormattedTextField. Тем не менее, решение требует немного больше работы, но аккуратнее.

JFormattedTextField по умолчанию не вызывает изменения свойства после каждого изменения текста в поле. Конструктор по умолчанию JFormattedTextField не создает средство форматирования.

Однако, чтобы сделать то, что предложил OP, вам нужно использовать средство форматирования, которое будет вызывать метод commitEdit() после каждого действительного редактирования поля. Метод commitEdit() - это то, что вызывает изменение свойства по сравнению с тем, что я вижу, и без средства форматирования, оно запускается по умолчанию при изменении фокуса или при нажатии клавиши ввода.

Подробнее см. http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value.

Создание объекта форматирования по умолчанию (DefaultFormatter) для передачи в JFormattedTextField либо через его конструктор, либо через метод установки. Один из методов форматирования по умолчанию - setCommitsOnValidEdit(boolean commit), который устанавливает для форматера запуск метода commitEdit() каждый раз, когда изменяется текст. Затем его можно получить с помощью PropertyChangeListener и propertyChange() метода.

1 голос
/ 15 декабря 2017
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();
   } 
});

Но я бы не просто разбирал что-либо, к чему пользователь (возможно, случайно) прикасался к своей клавиатуре, в Integer. Вы должны поймать все броски Exception и убедиться, что JTextField не пусто.

1 голос
/ 02 ноября 2014

это была обновленная версия Codemwnci. его код довольно хорошо и отлично работает, за исключением сообщения об ошибке. Чтобы избежать ошибки, вы должны изменить условие условия.

  // 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 голос
/ 21 октября 2013

Вы можете использовать даже «MouseExited» для контроля. Пример:

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

        }

    }   
0 голосов
/ 01 марта 2019

Если мы используем метод runnable SwingUtilities.invokeLater () при использовании приложения прослушивания документов, иногда оно застревает и требует времени для обновления результата (согласно моему эксперименту).Вместо этого мы также можем использовать событие KeyReleased для прослушивателя изменения текстового поля, как упоминалось здесь .

usernameTextField.addKeyListener(new KeyAdapter() {
        public void keyReleased(KeyEvent e) {
            JTextField textField = (JTextField) e.getSource();
            String text = textField.getText();
            textField.setText(text.toUpperCase());
        }
    });
0 голосов
/ 21 июня 2017

DocumentFilter ? Это дает вам возможность манипулировать.

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

Извините. Я использую Jython (Python в Java) - но легко понять

# 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));
...