JavaFX 2.2 TextField maxlength
Solution 1
This is a better way to do the job on a generic text field:
public static void addTextLimiter(final TextField tf, final int maxLength) {
tf.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(final ObservableValue<? extends String> ov, final String oldValue, final String newValue) {
if (tf.getText().length() > maxLength) {
String s = tf.getText().substring(0, maxLength);
tf.setText(s);
}
}
});
}
Works perfectly, except for that Undo bug.
Solution 2
With java8u40 we got a new class TextFormatter: one of its main responsibilities is to provide a hook into any change of text input before it gets comitted to the content. In that hook we can accept/reject or even change the proposed change.
The requirement solved in the OP's self-answer is
- the rule: restrict the length of text to shorter than n chars
- the modification: if the rule is violated, keep the last n chars as the input text and remove the excess chars at its start
Using a TextFormatter, this could be implemented like:
// here we adjust the new text
TextField adjust = new TextField("scrolling: " + len);
UnaryOperator<Change> modifyChange = c -> {
if (c.isContentChange()) {
int newLength = c.getControlNewText().length();
if (newLength > len) {
// replace the input text with the last len chars
String tail = c.getControlNewText().substring(newLength - len, newLength);
c.setText(tail);
// replace the range to complete text
// valid coordinates for range is in terms of old text
int oldLength = c.getControlText().length();
c.setRange(0, oldLength);
}
}
return c;
};
adjust.setTextFormatter(new TextFormatter(modifyChange));
Asides:
- modifying a property while listening to its change might lead to unexpected side-effects
- all suggested solutions on the key-level events are broken (they can't handle paste/programatic changes
Solution 3
You can do something similar to approach described here: http://fxexperience.com/2012/02/restricting-input-on-a-textfield/
class LimitedTextField extends TextField {
private final int limit;
public LimitedTextField(int limit) {
this.limit = limit;
}
@Override
public void replaceText(int start, int end, String text) {
super.replaceText(start, end, text);
verify();
}
@Override
public void replaceSelection(String text) {
super.replaceSelection(text);
verify();
}
private void verify() {
if (getText().length() > limit) {
setText(getText().substring(0, limit));
}
}
};
Solution 4
The full code i used to solve my problem is the code below. I extend the TextField class like Sergey Grinev done and i added an empty constructor. To set the maxlength i added a setter method. I first check and then replace the text in the TextField because i want to disable inserting more than maxlength characters, otherwise the maxlength + 1 character will be inserted at the end of the TextField and the first charcter of the TextField will be deleted.
package fx.mycontrols;
public class TextFieldLimited extends TextField {
private int maxlength;
public TextFieldLimited() {
this.maxlength = 10;
}
public void setMaxlength(int maxlength) {
this.maxlength = maxlength;
}
@Override
public void replaceText(int start, int end, String text) {
// Delete or backspace user input.
if (text.equals("")) {
super.replaceText(start, end, text);
} else if (getText().length() < maxlength) {
super.replaceText(start, end, text);
}
}
@Override
public void replaceSelection(String text) {
// Delete or backspace user input.
if (text.equals("")) {
super.replaceSelection(text);
} else if (getText().length() < maxlength) {
// Add characters, but don't exceed maxlength.
if (text.length() > maxlength - getText().length()) {
text = text.substring(0, maxlength- getText().length());
}
super.replaceSelection(text);
}
}
}
Inside the fxml file i added the import (of the package that the TextFieldLimited class is existing) on the top of the file and replace the TextField tag with the custom TextFieldLimited.
<?import fx.mycontrols.*?>
.
.
.
<TextFieldLimited fx:id="usernameTxtField" promptText="username" />
Inside the controller class,
on the top (property declaration),
@FXML
private TextFieldLimited usernameTxtField;
inside the initialize method,
usernameTxtField.setLimit(40);
That's all.
Solution 5
I'm using a simpler way to both limit the number of characters and force numeric input:
public TextField data;
public static final int maxLength = 5;
data.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try {
// force numeric value by resetting to old value if exception is thrown
Integer.parseInt(newValue);
// force correct length by resetting to old value if longer than maxLength
if(newValue.length() > maxLength)
data.setText(oldValue);
} catch (Exception e) {
data.setText(oldValue);
}
}
});
Georgios Syngouroglou
Updated on February 18, 2021Comments
-
Georgios Syngouroglou about 3 years
I am working on a JavaFX 2.2 project and I have a problem using the
TextField
control. I want to limit the number of characters that a user will be able to enter into eachTextField
. However I can't find a property or something like maxlength. The same problem existed in Swing and was solved this way. How to solve it for JavaFX 2.2? -
keuleJ about 11 yearsWouldn't it be nice to first check and then replace the text?
-
Sergey Grinev about 11 yearsIt's possible, but logic will become too complex for a short sample.
-
Georgios Syngouroglou about 11 yearsTo make it work i added a 0-argument constructor and a setter method for limit property. Thank u.
-
Hardcoded almost 11 yearsCan you explain, why you are using
getText
before setting or replacing some of the text? Sounds wrong to me. Have you tested this with C&P? -
Georgios Syngouroglou almost 11 yearsI wanted to append new character input into the TextField, only if the length of the text will not be more than maxlength. That's why i am first checking if length has reached maxlength and then i append with the new character. The wrong part exists when user clicks on delete or backspace. I update the code for that. Also, i update replaceSelection() method, to prevent TextField to exceed maxlength when user pastes text which length, in addition with the existing text, is more than maxlength characters. I tested and it works fine.
-
Cooper over 10 yearsNot going to downvote, but this method will cause an
java.lang.IndexOutOfBoundsException
if you enter above themaxLength
and attempt to use system default Undo. -
hemisphire over 10 yearsGood point - I'm also using a technique like this now: stackoverflow.com/questions/13562712/…
-
Cooper over 10 yearsI opened my own question regarding this Undo bug. See stackoverflow.com/questions/19184305/… for a pretty good solution.
-
kleopatra over 8 yearsbeware: modifying the state of the sender during notification generally is a bad idea - you might get hard-to-track side-effects (the undo bug might be such a unwanted side-effect, though I didn't dig into it)
-
Lakshitha Kanchana over 7 years@ceklock Do u mind if I asked to explain, what happens when :::: String s = tf.getText().substring(0, maxLength); is replaced with s = oldValue; because I hard to understand it!
-
kleopatra over 4 yearsno - see my comment to the question (and/or my answer ;)