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!
Add a listener to the underlying Document, which is automatically created for you.
// 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);
}
}
});
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.)
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.
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.)
/**
* 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.
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!
Just create an interface that extends DocumentListener and implements all DocumentListener methods:
#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:
jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
// Your code here
});
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 :
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);
}
}
I know this relates to a really old problem, however, it caused me some problems too. As kleopatra responded in a comment above, I solved the problem with a JFormattedTextField. However, the solution requires a bit more work, but is neater.
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.
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.
See http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value for more details.
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.
An elegant way is to add the listener to the caret position, because it changes every time something is typed/deleted, then just compare old value with current one.
String oldVal = ""; // empty string or default value
JTextField tf = new JTextField(oldVal);
tf.addCaretListener(e -> {
String currentVal = tf.getText();
if(!currentVal.equals(oldVal)) {
oldVal = currentVal;
System.out.println("Change"); // do something
}
});
(This event is also being triggered every time a user just clicks into a TextField).
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();
}
});
But I would not just parse anything the user (maybe on accident) touches on his keyboard into an Integer. You should catch any Exceptions thrown and make sure the JTextField is not empty.
If we use runnable method SwingUtilities.invokeLater() while using Document listener application is getting stuck sometimes and taking time to update the result(As per my experiment). Instead of that we can also use KeyReleased event for text field change listener as mentioned here.
usernameTextField.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
JTextField textField = (JTextField) e.getSource();
String text = textField.getText();
textField.setText(text.toUpperCase());
}
});
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.
// 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);
}
}
});
You can use even "MouseExited" to control.
example:
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) {
}
}
Use a KeyListener (which triggers on any key) rather than the ActionListener (which triggers on enter)
DocumentFilter ? It gives you the ability to manipulate.
[ http://www.java2s.com/Tutorial/Java/0240__Swing/FormatJTextFieldstexttouppercase.htm ]
Sorry. J am using Jython (Python in Java) - but easy to understand
# 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));
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.
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:
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.
Here is a Kotlin port of #Boann's answer, which is a great solution that has been working well for me.
import java.beans.*
import javax.swing.*
import javax.swing.event.*
import javax.swing.text.*
/**
* Installs a listener to receive notification when the text of this
* [JTextComponent] is changed. Internally, it installs a [DocumentListener] on the
* text component's [Document], and a [PropertyChangeListener] on the text component
* to detect if the `Document` itself is replaced.
*
* #param changeListener a listener to receive [ChangeEvent]s when the text is changed;
* the source object for the events will be the text component
*/
fun JTextComponent.addChangeListener(changeListener: ChangeListener) {
val dl: DocumentListener = object : DocumentListener {
private var lastChange = 0
private var lastNotifiedChange = 0
override fun insertUpdate(e: DocumentEvent) = changedUpdate(e)
override fun removeUpdate(e: DocumentEvent) = changedUpdate(e)
override fun changedUpdate(e: DocumentEvent) {
lastChange++
SwingUtilities.invokeLater {
if (lastNotifiedChange != lastChange) {
lastNotifiedChange = lastChange
changeListener.stateChanged(ChangeEvent(this))
}
}
}
}
addPropertyChangeListener("document") { e: PropertyChangeEvent ->
(e.oldValue as? Document)?.removeDocumentListener(dl)
(e.newValue as? Document)?.addDocumentListener(dl)
dl.changedUpdate(null)
}
document?.addDocumentListener(dl)
}
You can use it on any text component as follows:
myTextField.addChangeListener { event -> myEventHandler(event) }
Like his code, also public domain.
public JoinChatClient(String serverAddress, String chatName)
{
chatWindow.getContentPane().add(sendButton, "South");
chatWindow.getContentPane().add(splitPane, "Center");
chatWindow.setSize(800,500);
sendButton.addActionListener(this);
chatWindow.setTitle("Chat Room");
chatWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
splitPane.setDividerLocation(350);
sendButton.setBackground(Color.gray);
sendButton.setForeground(Color.red);
outChatTextArea.setEditable(false);
inChatTextArea.setFont (new Font("default",Font.ITALIC,20));
outChatTextArea.setFont(new Font("default",Font.BOLD,20));
inChatTextArea.setLineWrap(true);
outChatTextArea.setLineWrap(true);
inChatTextArea.setWrapStyleWord(true);
outChatTextArea.setWrapStyleWord(true);
inChatTextArea.setText("Enter text to be sent here.");
outChatTextArea.setText("You can move the separator bar!");
inChatTextArea.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
if(inChatTextArea.getText().equals("Enter text to be sent here."))
{
inChatTextArea.setText("");
inChatTextArea.setFont(new Font("default",Font.BOLD,20));
}
}
public void focusLost(FocusEvent e) {
if(inChatTextArea.getText().isEmpty())
{
inChatTextArea.setFont (new Font("default",Font.ITALIC,20));
inChatTextArea.setText("Enter text to be sent here.");
}
}
});
chatWindow.getRootPane().setDefaultButton(sendButton);
chatWindow.setVisible(true);
}
I've looked over all the threads I could find concerning this, and I cannot figure out why hitting ENTER doesn't activate the actionPerformed method attached to sendButton. Is it because the text field has a FocusListener?
Things I've tried:
changing the statement to target the specific text field (inChatTextArea)
moved the setVisible statement to the end
targeted different parts of the GUI when hitting enter
Bear in mind I've only included the code that builds the GUI in an attempt to waste less of your time.
What I want: Ideally, I want to keep my FocusListener (or something like it) so that I can display the "text field hint." I would like to be able to hit ENTER to send the user's text while the inChatTextArea field is focused.
If a component on the JFrame has focus, and can accept an enter key press, such as one of the JTextAreas, then the enter presses will go to that component and not to the default button. For the default button to work, then the JFrame or the button or some other component that does not accept enter key presses, needs to have focus. I'm guessing that one of your JTextAreas has stolen the focus, and that this is messing you up.
This question is old, but I found it when having the same issue. So I hope others might find it useful.
I figured out that getRootPane() will return null if the component is trying to access the root pane too early, e.g. under construction of the component.
Hence, I propose to use SwingUtilities.invoke(Runnable) to postpone setting the default button on the root pane, and also to request the focus to the button.
So this method could be a helper method on a class to extend from:
protected void setDefaultButton(JButton button) {
// Uses invoke later, as getRootPane() might return null if the method is called under construction
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JRootPane rootPane = getRootPane();
if (rootPane != null) {
rootPane.setDefaultButton(button);
}
button.requestFocus(); // set the focus on the button
}
});
}
I have a JTable with four columns, the first one containing either a number or a text, the other three only text. I'm trying to filter this table with the help of a RowFilter:
sorter = new TableRowSorter<TableModel>(myOwnTableModel);
The checkboxFilter I got works well enough:
sorter.setRowFilter(RowFilter.regexFilter("^[0-9]$", 0));
This sorter is activated or deactivate depending on a checkbox that is either set or not.
The second filtering happens if a user puts some text in a textfield. For itself, this works fine already:
String regex = "(?i)" + Pattern.quote(s); // s = input Text of user
sorter.setRowFilter(RowFilter.regexFilter(regex, 1,2,3));
What I can't do, is to activate both filters at the same time. Maybe I'm thinking way too far, my idea has been to "concatenate" the two filters, the checkboxFilter should be "and" the other "or". I tried several things, to me the most promising looked something like:
String regex = "(?i)" + Pattern.quote(s);
bookFilter = RowFilter.regexFilter(regex, 1,2,3);
sorter.setRowFilter(bookFilter.andFilter(Arrays.asList(
RowFilter.regexFilter("^[0-9]$", 0))));
Unfortunately, this doesn't lead to any usable result. Any ideas appreciated :)
The solution is to add an ActionListener to the JCheckBox to update the filter state if the checkbox is toggled and to add a DocumentListener to the JTextField's underlying Document to update the filter state if the contents of the field is updated.
The other bug in your code is that you are calling the static andFilter method on your bookFilter instance and are only passing in the newly constructed regex filter (i.e. you are only passing in one parameter to andFilter). The correct usage is:
RowFilter andFilter = RowFilter.andFilter(filter1, filter2, etc);
Example Event Listeners
JCheckBox cb = ...
cb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
updateFilters();
}
});
JTextField tf = ...
tf.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) { updateFilters(); }
public void removeUpdate(DocumentEvent e) { updateFilters(); }
publci void changedUpdate(DocumentEvent e) { updateFilters(); }
});
... and then define your updateFilters() method to install a new filter based on when the checkbox is selected and whether the text field is empty or not.
Example Filter Update Method
public void updateFilters() {
if (cb.isSelected()) {
if (tf.getText().length() > 0) {
// Both filters active so construct an and filter.
sorter.setRowFilter(RowFilter.andFilter(bookFilter, checkBoxFilter));
} else {
// Checkbox selected but text field empty.
sorter.setRowFilter(checkBoxFilter);
}
} else if (tf.getText().length() > 0) {
// Checkbox deselected but text field non-empty.
sorter.setRowFilter(bookFilter);
} else {
// Neither filter "active" so remove filter from sorter.
sorter.setRowFilter(null); // Will cause table to re-filter.
}
}
I'm writing an applet and want to figure out how to make a button and a key event cover the same bit of code. For this question, I'll call this button fireButton. The code for the action event would of course look like this:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fireButton) {
//all the code that pressing button executes
}
}
Now, I want pressing the 'enter' key to perform the same code that the action event handles, but I do not want to rewrite all the code again in a keyPressed method.
To be specific, I'm writing a battleship program, and the 'Fire' button takes input from two textFields, handles exceptions, and passes the input as parameters to a method that fires at a particular square in the grid. Ideally, pressing the enter key would function the same way as if I had pressed the fire button. Is there a way to make a certain method call an actionPerformed method? If not, what would be an elegant solution to the problem?
Create an Action
Add the Action to the JButton
Use Key Bindings to bind the Enter key to the Action
Read the Swing tutorial. There are sections on:
How to Use Actions
How to Use Key Bindings
If you are just talking about invoking the "Fire" button with the enter key then check out Enter Key and Button for a couple of approaches.
I suggest you put all the code in a separate method that receives all the relevant data from the event (if any) as parameters:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fireButton) {
Object relevantData0 = new Object(); // e.getSomething();
Object relevantData1 = new Object(); // e.getSomethingElse();
handleFireAction(relevantData1, relevantData2);
}
}
public void actionPerformed(KeyEvent e) {
if (e.getSource() == fireButton) {
Object relevantData0 = new Object(); // e.getSomething();
Object relevantData1 = new Object(); // e.getSomethingElse();
handleFireAction(relevantData1, relevantData2);
}
}
private void handleFireAction(Object relevantData0, Object relevantData1) { // Object relevantDat2, and so on
//all the code that pressing button executes
}
If you don't need any data from the event its even easier ;)
This way you just write your code once for both events. It's a general OO aproach.
Hope this helps.
Borrowing from MVC I would recommend you have a controller class which handles these sorts of requests. Then all you have to do is delegate to the controller in each event handler.
Like so:
public class BattleShipController {
public void handleFireAction() {
// ...
}
}
//-- in your UI class(es)
private BattleShipController _controller = new BattleShipController();
//-- in event calls:
_controller.handleFireAction();
If you post relevant code I can make further suggestions.