[java] 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);
        }
    }
}

도움을 주시면 감사하겠습니다!



답변

기본 문서에 리스너를 추가하면 자동으로 작성됩니다.

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


답변

이에 대한 일반적인 대답은 “use DocumentListener“입니다. 그러나 항상 인터페이스가 번거 롭다는 것을 알았습니다. 진실로 인터페이스는 오버 엔지니어링되었습니다. 텍스트를 삽입, 제거 및 대체하는 방법에는 대체 방법이 하나만 필요한 경우 세 가지가 있습니다. (삽입은 텍스트가없는 텍스트를 대체하는 것으로 볼 수 있으며 제거는 텍스트가없는 일부 텍스트를 대체하는 것으로 볼 수 있습니다.)

일반적으로 당신이 원하는 모든입니다 아는 것입니다 상자의 텍스트가 변경되었을 때 전형적인 있도록,DocumentListener 구현에는 하나의 메서드를 호출하는 세 가지 메서드가 있습니다.

따라서 다음 유틸리티 방법을 사용하여 a 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);
}

문서에 리스너를 직접 추가하는 것과 달리, 이것은 텍스트 구성 요소에 새 문서 객체를 설치하는 (흔하지 않은) 경우를 처리합니다. 또한 Jean-Marc Astesana의 답변 에서 언급 한 문제를 해결 하여 문서가 때로는 필요한 것보다 많은 이벤트를 발생시킵니다.

어쨌든이 방법을 사용하면 다음과 같은 성가신 코드를 대체 할 수 있습니다.

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를 확장하고 모든 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
});


답변

사용자가 필드를 수정할 때 DocumentListener는 때때로 두 개의 이벤트를 수신 할 수 있습니다. 예를 들어 사용자가 전체 필드 내용을 선택한 다음 키를 누르면 removeUpdate (모든 내용이 제거됨) 및 insertUpdate가 수신됩니다. 귀하의 경우, 나는 그것이 문제라고 생각하지 않지만 일반적으로 말하자면 문제입니다. 불행히도 JTextField를 서브 클래스 화하지 않고 textField의 내용을 추적 할 수있는 방법이없는 것 같습니다. 다음은 “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);
        }
    }


답변

나는 이것이 정말로 오래된 문제와 관련이 있다는 것을 알고 있지만, 나도 약간의 문제를 일으켰다. 로 클레오 파트라가 위의 코멘트에 응답, 나는 함께 문제를 해결 JFormattedTextField. 그러나 솔루션에는 약간의 작업이 더 필요하지만 더 깔끔합니다.

JFormattedTextField모든 텍스트 필드에 변경 후 기본 트리거에 의해 속성을 변경하지 않습니다. 기본 생성자 JFormattedTextField는 포맷터를 만들지 않습니다.

그러나 OP가 제안한 작업을 수행 commitEdit()하려면 필드를 유효하게 편집 할 때마다 메소드 를 호출하는 포맷터를 사용해야 합니다. 그만큼commitEdit() 방법은 내가 볼 수있는 것과 포맷터없이 속성 변경을 트리거하는 것입니다. 포커스 변경이나 Enter 키를 누를 때 기본적으로 트리거됩니다.

자세한 내용은 http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value 를 참조하십시오.

생성자 또는 setter 메소드를 통해 DefaultFormatter전달할 기본 포맷터 ( ) 오브젝트를 작성하십시오 JFormattedTextField. 기본 포맷터의 한 가지 방법은 텍스트가 변경 될 때마다 메소드 setCommitsOnValidEdit(boolean commit)가 트리거되도록 포맷터를 설정하는 것 commitEdit()입니다. 그런 다음 a PropertyChangeListenerpropertyChange()방법을 사용하여 선택할 수 있습니다 .


답변

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비어 있지 않은지 확인하십시오 .


답변

문서 리스너 응용 프로그램을 사용하는 동안 실행 가능한 메소드 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());
    }
});