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 PropertyChangeListenerpropertyChange()방법을 사용하여 선택할 수 있습니다 .


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.