Java JTextField 的值更改侦听器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3953208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Value Change Listener to JTextField
提问by user236501
I want the message box to appear immediately after the user changes the value in the textfield. Currently, I need to hit the enter key to get the message box to pop out. Is there anything wrong with my code?
我希望在用户更改文本字段中的值后立即显示消息框。目前,我需要按回车键才能弹出消息框。我的代码有什么问题吗?
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);
}
}
}
Any help would be appreciated!
任何帮助,将不胜感激!
采纳答案by Codemwnci
Add a listener to the underlying Document, which is automatically created for you.
向底层 Document 添加一个侦听器,它是自动为您创建的。
// 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);
}
}
});
回答by Jean-Marc Astesana
Be aware that when the user modify the field, the DocumentListener can, sometime, receive two events. For instance if the user selects the whole field content, then press a key, you'll receive a removeUpdate (all the content is remove) and an insertUpdate. In your case, I don't think it is a problem but, generally speaking, it is. Unfortunately, it seems there's no way to track the content of the textField without subclassing JTextField. Here is the code of a class that provide a "text" property :
请注意,当用户修改字段时,DocumentListener 有时会接收两个事件。例如,如果用户选择整个字段内容,然后按下一个键,您将收到一个 removeUpdate(所有内容都被删除)和一个 insertUpdate。在你的情况下,我不认为这是一个问题,但一般来说,它是。不幸的是,如果不继承 JTextField,似乎无法跟踪 textField 的内容。这是提供“文本”属性的类的代码:
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);
}
}
回答by Astridax
I know this relates to a really old problem, however, it caused me some problems too. As kleopatraresponded in a comment above, I solved the problem with a JFormattedTextField
. However, the solution requires a bit more work, but is neater.
我知道这与一个非常老的问题有关,但是,它也给我带来了一些问题。正如kleopatra在上面的评论中回应的那样,我用JFormattedTextField
. 然而,该解决方案需要更多的工作,但更简洁。
The JFormattedTextField
doesn't by default trigger a property change after every text changes in the field. The default constructor of JFormattedTextField
does not create a formatter.
JFormattedTextField
默认情况下,不会在字段中的每个文本更改后触发属性更改。的默认构造函数JFormattedTextField
不创建格式化程序。
However, to do what the OP suggested, you need to use a formatter which will invoke the commitEdit()
method after each valid edit of the field. The commitEdit()
method is what triggers the property change from what I can see and without the formatter, this is triggered by default on a focus change or when the enter key is pressed.
但是,要执行 OP 建议的操作,您需要使用格式化程序,该格式化程序将commitEdit()
在每次对该字段进行有效编辑后调用该方法。该commitEdit()
方法是从我可以看到的和没有格式化程序的情况下触发属性更改的原因,默认情况下会在焦点更改或按下 Enter 键时触发。
See http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#valuefor more details.
有关更多详细信息,请参阅http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value。
Create a default formatter (DefaultFormatter
) object to be passed to the JFormattedTextField
either via its constructor or a setter method. One method of the default formatter is setCommitsOnValidEdit(boolean commit)
, which sets the formatter to trigger the commitEdit()
method every time the text is changed. This can then be picked up using a PropertyChangeListener
and the propertyChange()
method.
创建DefaultFormatter
要JFormattedTextField
通过其构造函数或 setter 方法传递给 的默认格式化程序 ( ) 对象。默认格式化程序的一种方法是setCommitsOnValidEdit(boolean commit)
,它设置格式化程序在commitEdit()
每次更改文本时触发该方法。然后可以使用 aPropertyChangeListener
和propertyChange()
方法来获取它。
回答by fishgold192
You can use even "MouseExited" to control. example:
您甚至可以使用“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) {
}
}
回答by nick
Use a KeyListener (which triggers on any key) rather than the ActionListener (which triggers on enter)
使用 KeyListener(在任何键上触发)而不是 ActionListener(在输入时触发)
回答by Bipul Chandra Dev Nath
it was the update version of Codemwnci. his code is quite fine and works great except the error message. To avoid error you must change the condition statement.
它是 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);
}
}
});
回答by Boann
The usual answer to this is "use a DocumentListener
". However, I always find that interface cumbersome. Truthfully the interface is over-engineered. It has three methods, for insertion, removal, and replacement of text, when it only needs one method: replacement. (An insertion can be viewed as a replacement of no text with some text, and a removal can be viewed as a replacement of some text with no text.)
通常的答案是“使用一个DocumentListener
”。但是,我总是觉得那个界面很麻烦。说实话,界面是过度设计的。它有三种方法,用于文本的插入、移除和替换,当它只需要一种方法时:替换。(插入可以看作是用一些文本替换无文本,删除可以看作是用无文本替换一些文本。)
Usually all you want is to know is when the text in the box has changed, so a typical DocumentListener
implementation has the three methods calling one method.
通常您只想知道框中的文本何时发生变化,因此典型的DocumentListener
实现具有调用一个方法的三个方法。
Therefore I made the following utility method, which lets you use a simpler ChangeListener
rather than a DocumentListener
. (It uses Java 8's lambda syntax, but you can adapt it for old Java if needed.)
因此我制作了以下实用方法,它可以让您使用更简单的ChangeListener
而不是DocumentListener
. (它使用 Java 8 的 lambda 语法,但如果需要,您可以针对旧 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);
}
Unlike with adding a listener directly to the document, this handles the (uncommon) case that you install a new document object on a text component. Additionally, it works around the problem mentioned in Jean-Marc Astesana's answer, where the document sometimes fires more events than it needs to.
与直接向文档添加侦听器不同,这可以处理在文本组件上安装新文档对象的(不常见)情况。此外,它解决了Jean-Marc Astesana 的回答中提到的问题,其中文档有时会触发比它需要的更多的事件。
Anyway, this method lets you replace annoying code which looks like this:
无论如何,这个方法可以让你替换看起来像这样的烦人的代码:
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();
}
});
With:
和:
addChangeListener(someTextBox, e -> doSomething());
Code released to public domain. Have fun!
代码发布到公共领域。玩得开心!
回答by RocketMan
I am brand new to WindowBuilder, and, in fact, just getting back into Java after a few years, but I implemented "something", then thought I'd look it up and came across this thread.
我是 WindowBuilder 的新手,事实上,几年后才回到 Java,但我实现了“一些东西”,然后想我会查找它并遇到这个线程。
I'm in the middle of testing this, so, based on being new to all this, I'm sure I must be missing something.
我正在测试这个,所以,基于对这一切的新手,我确定我一定遗漏了一些东西。
Here's what I did, where "runTxt" is a textbox and "runName" is a data member of the class:
这是我所做的,其中“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);
}
}
Seems a lot simpler than what's here so far, and seems to be working, but, since I'm in the middle of writing this, I'd appreciate hearing of any overlooked gotchas. Is it an issue that the user could enter & leave the textbox w/o making a change? I think all you've done is an unnecessary assignment.
似乎比到目前为止的要简单得多,并且似乎正在起作用,但是,由于我正在撰写本文,我很高兴听到任何被忽视的问题。用户可以在不进行更改的情况下进入和离开文本框是否存在问题?我认为你所做的只是一个不必要的任务。
回答by Andrey Megvinov
Just create an interface that extends DocumentListener and implements all DocumentListener methods:
只需创建一个扩展 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);
}
}
and then:
进而:
jTextField.getDocument().addDocumentListener(new SimpleDocumentListener() {
@Override
public void update(DocumentEvent e) {
// Your code here
}
});
or you can even use lambda expression:
或者你甚至可以使用 lambda 表达式:
jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
// Your code here
});
回答by papuga
DocumentFilter? It gives you the ability to manipulate.
文件过滤器?它给了你操纵的能力。
[ http://www.java2s.com/Tutorial/Java/0240__Swing/FormatJTextFieldstexttouppercase.htm]
[ http://www.java2s.com/Tutorial/Java/0240__Swing/FormatJTextFieldstexttouppercase.htm]
Sorry. J am using Jython (Python in Java) - but easy to understand
对不起。J 正在使用 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));