Прослушиватель изменения значения в JTextField

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

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

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

11 ответов


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

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

обычный ответ на это "использовать 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());

код выпущен в общественное достояние. Получайте удовольствие!


имейте в виду, что когда пользователь изменяет поле, DocumentListener может иногда получать два события. Например, если пользователь выбирает все содержимое поля, а затем нажимает клавишу, вы получите removeUpdate (все содержимое удаляется) и insertUpdate. В вашем случае, я не думаю, что это проблема, но, вообще говоря, это. К сожалению, кажется, что нет способа отслеживать содержимое текстового поля без подкласса JTextField. Вот код класса, который обеспечивает свойство "текст":

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

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

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

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

см http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value Для получения более подробной информации.

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


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

вы можете использовать даже "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) {

        }

    }   

Это была обновленная версия 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);
     }
  }
});

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. Вы должны поймать любой Exceptions брошен и убедитесь, что JTextField - это не пустые.


используйте KeyListener (который срабатывает на любом ключе), а не ActionListener (который срабатывает при вводе)


Я новичок в WindowBuilder, и, на самом деле, просто возвращаюсь в Java через несколько лет, но я реализовал "что-то", затем подумал, что посмотрю его и наткнулся на этот поток.

Я в середине тестирования, поэтому, основываясь на том, что я новичок во всем этом, я уверен, что я что-то пропустил.

вот что я сделал, где "runTxt" - это текстовое поле, а "runName" - член данных класса:

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

кажется намного проще, чем здесь, и, кажется, работает, но, поскольку я в середине написания этого, я был бы признателен, если бы услышал о любых упущенных gotchas. Это проблема, что пользователь может ввести и оставить текстовое поле без внесения изменений? Я думаю, что все, что вы сделали, это ненужное задание.


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

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

извините. J я использую 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));