Trình nghe thay đổi giá trị cho JTextField


215

Tôi muốn hộp thông báo xuất hiện ngay sau khi người dùng thay đổi giá trị trong trường văn bản. Hiện tại, tôi cần nhấn phím enter để hộp thông báo bật ra. Có bất cứ điều gì sai với mã của tôi?

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

Bất kỳ trợ giúp sẽ được đánh giá cao!

Câu trả lời:


373

Thêm một người nghe vào Tài liệu cơ bản, được tạo tự động cho bạn.

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

định dạng tốt cho cảnh báo / loại diễn viên. Mẫu tương tự sẽ hữu ích để xử lý số tiền gấp đôi (số liệu / giá bán được nhập hoặc hiển thị)
Max West

nó hoạt động tốt nhưng tôi có một truy vấn rằng, khi tôi chèn một số văn bản vào trường văn bản thì tôi muốn gọi một phương thức. tôi không có nhiều ý tưởng về cách nó được thực hiện ..

Tôi gặp vấn đề với JTable khi không nhận được các bản cập nhật hộp văn bản từ một JComboBox có thể chỉnh sửa khi nhấp vào một ô bảng khác và hàm insertUpdate ở đây là cách duy nhất để làm cho nó hoạt động chính xác.
winchella 7/10/2015

14
"Lỗi massage"
ungato

51

Câu trả lời thông thường cho điều này là "sử dụng một DocumentListener". Tuy nhiên, tôi luôn thấy giao diện đó cồng kềnh. Thực sự giao diện được thiết kế quá mức. Nó có ba phương thức, để chèn, xóa và thay thế văn bản, khi nó chỉ cần một phương thức: thay thế. (Một phần chèn có thể được xem như là sự thay thế không có văn bản với một số văn bản và việc xóa có thể được xem như là sự thay thế của một số văn bản không có văn bản.)

Thông thường tất cả những gì bạn muốn là biết khi văn bản trong hộp đã thay đổi , vì vậy một DocumentListenertriển khai điển hình có ba phương thức gọi một phương thức.

Do đó tôi đã thực hiện phương thức tiện ích sau, cho phép bạn sử dụng đơn giản ChangeListenerhơn là a DocumentListener. (Nó sử dụng cú pháp lambda của Java 8, nhưng bạn có thể điều chỉnh nó cho Java cũ nếu cần.)

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

Không giống như thêm người nghe trực tiếp vào tài liệu, điều này xử lý trường hợp (không phổ biến) mà bạn cài đặt một đối tượng tài liệu mới trên một thành phần văn bản. Ngoài ra, nó hoạt động xung quanh vấn đề được đề cập trong câu trả lời của Jean-Marc Astesana , trong đó tài liệu đôi khi phát sinh nhiều sự kiện hơn mức cần thiết.

Dù sao, phương pháp này cho phép bạn thay thế mã gây phiền nhiễu trông như thế này:

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

Với:

addChangeListener(someTextBox, e -> doSomething());

Mã được phát hành cho miền công cộng. Chúc vui vẻ!


5
Giải pháp tương tự: tạo một abstract class DocumentChangeListener implements DocumentListenerphương thức trừu tượng bổ sung change(DocumentEvent e)mà bạn gọi từ cả 3 phương thức khác. Có vẻ rõ ràng hơn đối với tôi vì nó sử dụng ít nhiều logic giống như abstract *Adapterngười nghe.
geronimo

+1 dưới dạng changedUpdatephương thức sẽ được gọi một cách rõ ràng thông qua một cuộc gọi trong mỗi insertUpdateremoveUpdate, để làm cho nó hoạt động ..
Kais

16

Chỉ cần tạo một giao diện mở rộng DocumentListener và thực hiện tất cả các phương thức 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);
    }
}

và sau đó:

jTextField.getDocument().addDocumentListener(new SimpleDocumentListener() {
    @Override
    public void update(DocumentEvent e) {
        // Your code here
    }
});

hoặc thậm chí bạn có thể sử dụng biểu thức lambda:

jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
    // Your code here
});

1
Đừng quên rằng giải pháp này yêu cầu một lớp trừu tượng thay vì giao diện trong tất cả các phiên bản trước Java 8.
klaar

15

Xin lưu ý rằng khi người dùng sửa đổi trường, đôi khi DocumentListener có thể nhận được hai sự kiện. Chẳng hạn, nếu người dùng chọn toàn bộ nội dung trường, sau đó nhấn phím, bạn sẽ nhận được removeUpdate (tất cả nội dung được xóa) và insertUpdate. Trong trường hợp của bạn, tôi không nghĩ đó là một vấn đề, nhưng nói chung, nó là. Thật không may, dường như không có cách nào để theo dõi nội dung của textField mà không phân lớp JTextField. Đây là mã của một lớp cung cấp thuộc tính "văn bản":

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 đã một loại textField ánh xạ các thay đổi tài liệu thành một thuộc tính - nó được gọi là JFormattedTextField :-)
kleopatra

11

Tôi biết điều này liên quan đến một vấn đề thực sự cũ, tuy nhiên, nó cũng gây ra cho tôi một số vấn đề. Khi kleopatra trả lời trong một bình luận ở trên, tôi đã giải quyết vấn đề với a JFormattedTextField. Tuy nhiên, giải pháp đòi hỏi nhiều công việc hơn một chút, nhưng gọn gàng hơn.

Theo JFormattedTextFieldmặc định, không kích hoạt thay đổi thuộc tính sau mỗi thay đổi văn bản trong trường. Hàm tạo mặc định của JFormattedTextFieldkhông tạo một bộ định dạng.

Tuy nhiên, để làm những gì OP đề xuất, bạn cần sử dụng một trình định dạng sẽ gọi commitEdit()phương thức sau mỗi lần chỉnh sửa trường hợp lệ. Các commitEdit()phương pháp là những gì gây nên sự thay đổi sở hữu từ những gì tôi có thể nhìn thấy và không có định dạng, điều này được kích hoạt theo mặc định trên một sự thay đổi trọng tâm hoặc khi nhập phím được nhấn.

Xem http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value để biết thêm chi tiết.

Tạo một DefaultFormatterđối tượng formatter ( ) mặc định được truyền cho JFormattedTextFieldhoặc thông qua hàm tạo của nó hoặc một phương thức setter. Một phương thức của trình định dạng mặc định là setCommitsOnValidEdit(boolean commit), thiết lập trình định dạng để kích hoạt commitEdit()phương thức mỗi khi văn bản được thay đổi. Điều này sau đó có thể được chọn bằng cách sử dụng a PropertyChangeListenerpropertyChange()phương thức.


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

Nhưng tôi sẽ không phân tích bất cứ thứ gì mà người dùng (có thể vô tình) chạm vào bàn phím của anh ấy thành một Integer. Bạn nên bắt bất kỳ Exceptions ném nào và đảm bảo JTextFieldnó không trống.


2

Nếu chúng tôi sử dụng phương thức runnable SwingUtilities.invokeLater () trong khi sử dụng ứng dụng Trình nghe tài liệu đôi khi bị kẹt và mất thời gian để cập nhật kết quả (Theo thử nghiệm của tôi). Thay vào đó, chúng ta cũng có thể sử dụng sự kiện KeyRelease cho trình nghe thay đổi trường văn bản như được đề cập ở đây .

usernameTextField.addKeyListener(new KeyAdapter() {
    public void keyReleased(KeyEvent e) {
        JTextField textField = (JTextField) e.getSource();
        String text = textField.getText();
        textField.setText(text.toUpperCase());
    }
});

1

đó là phiên bản cập nhật của Codemwnci. mã của anh ấy khá tốt và hoạt động tốt ngoại trừ thông báo lỗi. Để tránh lỗi, bạn phải thay đổi câu lệnh điều kiện.

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

Sự thích ứng của bạn kích hoạt hộp thoại thông báo lỗi bất cứ khi nào bất kỳ chuỗi nào dài hơn length = 0 được nhập vào trường văn bản. Vì vậy, về cơ bản đó là bất kỳ chuỗi nào khác ngoài một chuỗi trống. Đó không phải là giải pháp được yêu cầu.
klaar

0

Bạn có thể sử dụng ngay cả "MouseExited" để kiểm soát. thí dụ:

 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
không thực sự: yêu cầu là làm một cái gì đó khi văn bản được thay đổi - điều đó không liên quan đến mouseEvents ;-)
kleopatra

0

Tôi là một người hoàn toàn mới đối với WindowBuilder và trên thực tế, chỉ mới quay lại Java sau một vài năm, nhưng tôi đã thực hiện "một cái gì đó", sau đó nghĩ rằng tôi đã tìm kiếm nó và bắt gặp chủ đề này.

Tôi đang trong giai đoạn thử nghiệm điều này, vì vậy, dựa trên việc mới đối với tất cả những điều này, tôi chắc chắn rằng tôi phải thiếu một cái gì đó.

Đây là những gì tôi đã làm, trong đó "runTxt" là một hộp văn bản và "runName" là thành viên dữ liệu của lớp:

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

Có vẻ đơn giản hơn nhiều so với những gì ở đây cho đến nay và dường như đang hoạt động, nhưng, vì tôi đang ở giữa viết bài này, tôi đánh giá cao việc nghe thấy bất kỳ vấn đề bị bỏ qua nào. Đây có phải là vấn đề mà người dùng có thể nhập và rời khỏi hộp văn bản khi thực hiện thay đổi không? Tôi nghĩ rằng tất cả những gì bạn đã làm là một nhiệm vụ không cần thiết.


-1

Sử dụng KeyListener (kích hoạt trên bất kỳ khóa nào) thay vì ActionListener (kích hoạt khi nhập)


Điều này không hoạt động vì giá trị của trường không được ghi đúng, field.getText()trả về giá trị ban đầu. và sự kiện ( arg0.getKeyChar()) trả về việc kiểm tra lỗi nhấn phím là cần thiết để xác định xem bạn có nên nối với văn bản trường không.
lộng lẫy

@glend, bạn có thể sử dụng sự kiện keyRelired thay vì sự kiện keyTyped. Nó làm việc cho tôi và nhận được giá trị hoàn chỉnh.
Kakumanu siva krishna

-1

Bộ lọc tài liệu ? Nó cung cấp cho bạn khả năng thao tác.

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

Lấy làm tiếc. J đang sử dụng Jython (Python trong Java) - nhưng dễ hiểu

# 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));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.