Reputation: 19944
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 each TextField
. 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?
Upvotes: 18
Views: 59574
Reputation: 1
private void registerListener1(TextField tf1, TextField tf2,TextField tf3,TextField tf4) {
tf1.textProperty().addListener((obs, oldText, newText) -> {
if(newText.length() == 12) {
tf1.setText(newText.substring(0, 3));
tf2.setText(newText.substring(tf1.getText().length(), 6));
tf3.setText(newText.substring(tf1.getText().length()+tf2.getText().length(), 9));
tf4.setText(newText.substring(tf1.getText().length()+tf2.getText().length()+tf3.getText().length()));
tf4.requestFocus();
}
});
}
private void registerListener(TextField tf1, TextField tf2) {
tf1.textProperty().addListener((obs, oldText, newText) -> {
if(oldText.length() < 3 && newText.length() >= 3) {
tf2.requestFocus();
}
});
}
Upvotes: -1
Reputation: 549
I have this bit of code that only allows numbers and limits the input length on a text field in Javafx.
// Event handler for inputPrice
inputPrice.setOnAction(event2 -> {
// Obtain input as a String from text field
String inputPriceStr = inputPrice.getText();
// Get length of the String to compare with max length
int length = inputPrice.getText().length();
final int MAX = 10; // limit number of characters
// Validate user input allowing only numbers and limit input size
if (inputPriceStr.matches("[0-9]*") && length < MAX ) {
// your code here
}});
Upvotes: 0
Reputation: 51525
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
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:
Upvotes: 18
Reputation: 318
This method let TextField to finish all processing (copy/paste/undo safe). Do not requares to make extending class. And allow you to deside what to do with new text after every change (to push it to logic, or turn back to previous value, or even to modify it).
// fired by every text property change
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
For your case just add this logic inside. Works perfectly.
// For example 10 characters
if (newValue.length() >= 10) ((StringProperty)observable).setValue(oldValue);
Upvotes: 1
Reputation: 59
The code below will re-position the cursor so the user doesn't accidentally overwrite their input.
public static void setTextLimit(TextField textField, int length) {
textField.setOnKeyTyped(event -> {
String string = textField.getText();
if (string.length() > length) {
textField.setText(string.substring(0, length));
textField.positionCaret(string.length());
}
});
}
Upvotes: 2
Reputation: 19944
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.
Upvotes: 4
Reputation: 6352
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.
Upvotes: 24
Reputation: 1235
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);
}
}
});
Upvotes: 2
Reputation: 34508
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));
}
}
};
Upvotes: 8