JTextField에 대한 값 변경 리스너


215

사용자가 텍스트 필드의 값을 변경 한 직후 메시지 상자가 나타나기를 원합니다. 현재 메시지 상자가 나타나게하려면 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);
        }       
    }
}

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

답변:


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

경고 / 유형 캐스트에 적합한 형식입니다. 동일한 패턴이 이중 금액을 처리하는 데 유용합니다 (판매 수치 / 입력 또는 표시 가격)
— Max West

잘 작동하지만 쿼리를 텍스트 필드에 텍스트를 삽입하면 메서드를 호출하려고합니다. 어떻게되는지에 대해 잘

다른 테이블 셀을 클릭 할 때 편집 가능한 JComboBox에서 텍스트 상자 업데이트를 가져 오지 않는 JTable에 문제가 있었고 여기의 insertUpdate 함수가 제대로 작동하는 유일한 방법이었습니다.
— winchella

14
"오류 마사지"
— ungato

51

이에 대한 일반적인 대답은 "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());

공개 코드가 공개되었습니다. 즐기세요!


5
비슷한 해결책 : 다른 세 가지 메소드 모두에서 호출 abstract class DocumentChangeListener implements DocumentListener하는 추가 추상 메소드로 change(DocumentEvent e)를 작성하십시오. abstract *Adapter리스너 와 거의 동일한 논리를 사용하기 때문에 더 분명한 것 같습니다 .
— geronimo

같은 +1 changedUpdate방법은 각각 내에서 호출을 통해 명시 적으로 호출되어야 insertUpdate하고 removeUpdate작동 얻기 위해 ..
— 카이스

16

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가 수신됩니다. 귀하의 경우, 나는 그것이 문제라고 생각하지 않지만 일반적으로 말하자면 문제입니다. 불행히도 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);
        }
    }

3
Swing 에는 이미 문서 변경 사항을 속성에 매핑하는 textField 유형이 있습니다. JFormattedTextField :-)
— kleopatra

11

나는 이것이 정말로 오래된 문제와 관련이 있다는 것을 알고 있지만, 나도 약간의 문제를 일으켰다. 로 클레오 파트라가 위의 코멘트에 응답, 나는 함께 문제를 해결 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 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. Exception던진 것을 잡고 JTextField비어 있지 않은지 확인하십시오 .


2

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

텍스트 필드에 length = 0보다 긴 문자열을 입력 할 때마다 오류 메시지 대화 상자가 나타납니다. 기본적으로 빈 문자열 이외의 문자열입니다. 그것은 요청 된 솔루션이 아닙니다.
— klaar

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
실제로는 아니다 : 텍스트가 변경 될 때 요구 사항이 무언가를하고있다
— 마우스 이벤트

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

입력시 트리거되는 ActionListener 대신 KeyListener (모든 키에서 트리거)를 사용하십시오.


필드 값이 올바르게 캡처되지 않아 field.getText()초기 값을 반환 하기 때문에 작동하지 않습니다 . 그리고 이벤트 ( arg0.getKeyChar())는 키를 눌렀 음을 반환합니다. 필드 텍스트와 연결해야하는지 결정하기 위해 오류 검사가 필요합니다.
— glend

@glend에서는 keyTyped 이벤트 대신 keyReleased 이벤트를 사용할 수 있습니다. 그것은 나를 위해 일했고 완전한 가치를 얻었습니다.
— Kakumanu Siva krishna

-1

DocumentFilter ? 조작 할 수있는 기능을 제공합니다.

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

죄송합니다. Jython (Java의 Python)을 사용하고 있지만 이해하기 쉽습니다.

# 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));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.