Воспроизведение изменения значения в JTextField

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

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

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

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

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

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

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

Ответ 5

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

Ответ 6

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

        }

    }   

Ответ 7

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

Ответ 8

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

Ответ 9

Я совершенно новый для 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);
        }
    }

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

Ответ 10

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