Слушатель изменения значения на JTextField


215

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

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

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

Ответы:


373

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

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

хороший формат для предупреждения / типа приведения. Тот же шаблон будет полезен для обработки двойных сумм (цифры продаж / введенные или отображенные цены)
Макс Вест

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

У меня были проблемы с JTable, не получающим обновления текстового поля из редактируемого JComboBox при щелчке другой ячейки таблицы, и функция insertUpdate здесь была единственным способом заставить его работать должным образом.
Винчелла

14
«Ошибка массажа»
ungato

51

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

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


5
Аналогичное решение: создайте abstract class DocumentChangeListener implements DocumentListenerдополнительный абстрактный метод, change(DocumentEvent e)который вы вызываете из всех трех других методов. Мне кажется более очевидным, поскольку он использует более или менее ту же логику, что и abstract *Adapterслушатели.
Geronimo

+1 как changedUpdateметод должен быть вызван явно через вызов внутри каждого из, insertUpdateи removeUpdate, чтобы заставить его работать ..
Kais

17

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

1
Не забывайте, что это решение требует абстрактный класс вместо интерфейса во всех версиях до Java 8.
klaar

15

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

3
У Swing уже есть тип textField, который отображает изменения документа в свойстве - он называется JFormattedTextField :-)
kleopatra

11

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

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

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

См. Http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value для получения дополнительной информации.

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


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

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


2

Если мы используем метод 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());
    }
});

1

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

Ваша адаптация запускает диалоговое окно сообщения об ошибке всякий раз, когда в текстовое поле вводится любая строка длиннее длины = 0. Так что это в основном любая строка, кроме пустой строки. Это не запрошенное решение.
Клар

0

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

        }

    }   

6
не совсем: требование что-то делать, когда текст меняется - это не имеет отношения к mouseEvents ;-)
kleopatra

0

Я новичок в 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);
    }
}

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


-1

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


Это не работает, потому что значение поля не записывается должным образом, field.getText()возвращает начальное значение. и событие ( arg0.getKeyChar()) возвращает нажатие клавиши. Проверка ошибок необходима, чтобы определить, следует ли объединять текст поля.
встреча

@glend, вы можете использовать событие keyReleased вместо события keyTyped. Это сработало для меня и получить полную стоимость.
Какуману Шива Кришна

-1

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));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.