TextField that accepts only positive numbers - JavaFX - javafx

I'm trying to create a TextField in JavaFX (using Scene Builder) that accepts only positive numbers.
I'm trying actually to make a TextField for a 'Credit Card Number' which accepts 13 - 16 numeric values, not including the minus sign at the beginning, and which can accept 0 at the beginning of the text.
Also, since it's a 'Credit Card Number' TextField, I'm looking for a way where I can insert automatic space after each 4 letters/numbers inserted to it.
Note: I know how to create a TextField that accepts only numbers but it also accepts minus sign at the beginning and doesn't accept the 0 as the first input.

Here is what I have been using so my TextField only accepts digits and it works like a charm as below,
public static void digitsTxtFld(TextField field) {
field.setTextFormatter(new TextFormatter<Integer>(change -> {
String newText = change.getControlNewText();
if (newText.matches("\\d*")) {
return change;
}
return null;
}));
}
And to set a character limit I use this,
public static void setTextLimit(TextArea textArea, int length) {
textArea.setTextFormatter(new TextFormatter<>(change -> {
String string = change.getControlNewText();
if (string.length() > length) {
textArea.positionCaret(string.length());
return change;
} else {
return null;
}
}));
}
Edit:
Apparently one cannot have both text formatters as the second one you call will replace the first and your field will only run with one at a time. So I made a joint formatter as follows:
public static void setTextLimitToDigitFld(TextField field, int length) {
field.setTextFormatter(new TextFormatter<Integer>(change -> {
String newText = change.getControlNewText();
if (newText.matches("\\d*") && newText.length() > length) {
return change;
}
return null;
}));
}

Related

Sort vaadin grid containing text field as component column

I am using vaadin 8. This grid contains a number of columns. Two columns are having textfield as component column because user wants to enter something in string format. Hence we use TextField component inside both columns. This is done by using grid.addComponentColumn method. Even after enabling setSorting(true), it seems that sorting is not working for both these columns.
addComponentColumn(DataGrid::getUserMessage).setId("userMessage").setSortable(true).setCaption("UserMessage");
i have tried below two things but still it is not sorting.
First
addComponentColumn(DataGrid::getUserMessage).setId("userMessage").setSortable(true).setCaption("UserMessage").setComparator((p1, p2) -> p1.getUserMessage().getValue().compareTo(p2.getUserMessage().getValue()));
Second
addComponentColumn(DataGrid::getUserMessage).setId("userMessage").setSortable(true).setCaption("UserMessage").setSortOrderProvider(direction -> Arrays.asList(new QuerySortOrder("userMessage", direction)).stream());
Data grid is the class which contains column names and its setter/getters.
How can I make this work? Can someone demonstrate it by a snippet
Update below solution works! This piece of code is for improvement for sorting containin null values while using comparator
#Override
public int compare(final DataGrid a, final DataGrid b) {
if (a.getUserMessage().getIntegerValue() == null && b.getUserMessage().getIntegerValue() == null) {
return 0;
}
if (a.getUserMessage().getIntegerValue() == null) {
return -1;
}
if (b.getUserMessage().getIntegerValue() == null) {
return 1;
}
return a.getUserMessage().getIntegerValue().compareTo(b.getUserMessage().getIntegerValue());
}
);```
Here is a minimal example,
List<Person> personList = new ArrayList<>();
personList.add(new Person("Lucas", "Lucas Message"));
personList.add(new Person("Samuel", "Samuel Message"));
personList.add(new Person("Aaron", "Aaron Message"));
Grid<Person> grid = new Grid<>();
grid.setItems(personList);
grid.addColumn(Person::getName).setCaption("Name");
grid.addComponentColumn(person -> {
TextField tf = new TextField();
tf.setValue(person.getMessage());
tf.addValueChangeListener(e -> {
person.setMessage(e.getValue());
});
return tf;
}).setId("customColumn").setComparator(
(p1, p2) -> p1.getMessage().compareTo(p2.getMessage()))
.setCaption("Message");
And the Person class
public class Person {
private String name;
private String message;
public Person(String name, String message) {
setName(name);
setMessage(message);
}
// Getters and Setters
}

How to make a textField which only accepts integers but greater than 0

i am creating an program where the user has to enter the Number of dinnners on a Table which cant be zero , i am able to allow only integers as an input for the textField but how to exclude 0 and pop an error when user enters 0
A possibility to handle this simply use a ChangeListener for your textfield. In this posts its explained how to do it : Value Change Listener for JavaFX's TextField
For a range listener it should be sth like this:
TextField textField = new TextField();
textField.textProperty().addListener((observable, oldValue, newValue) -> {
int from = 0;
int to = 200;
if (newValue != null && !newValue.equals("")) {
try {
int number = Integer.parseInt(newValue);
if (number < from || number > to) {
throw new NumberFormatException();
}
} catch (NumberFormatException ignored) {
field.setText(oldValue);
}
}
});
This avoids the user to insert numbers bigger or smaller than you want. But its not a perfect way to do it (just written down fast).
I think this should work:
private void createListenerTextField (TextField textField, int LIMIT) {
UnaryOperator<TextFormatter.Change> integerFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches("-?([1-9][1-9]*)?")) {
return change;
}
return null;
};
textField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter(), null, integerFilter));
textField.lengthProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.intValue() > oldValue.intValue()) {
// Check if the new character is greater than LIMIT
if (textField.getText().length() >= LIMIT) {
// if it's LIMIT character then just setText to previous one
textField.setText(textField.getText().substring(0, LIMIT));
}
}
});
}
You can remove the LIMIT part if you want to let the user enter a huge number (I recommend to use it because the user can enter a bigint)

Javafx Textfield: Accept only digits of length 8 or 11 ( phone numbers)

Working on a project with javafx and I'm having a minor hitch. I want my textfield to accept only digits, 8 or 11 in length. Here's my code:
if(!txtPhone.getText().matches(.....) && (txtPhone.getText().length != 8 || txtPhone.getText(). length != 11){
System.out.print("Please enter a valid phone number");
}
Regular Expression can be used to create custom validations.
if (txtPhone.getText().matches("\\d{8}|\\d{11}") {
System.out.println("Its Valid Number");
//return true;
}else {
System.out.println("Invalid Input..!");
//return false;
}
You can learn and check about Regular Expressions Here
The task involves two stages:
You must first create a text box that accepts digits only (up to 11 maximum).
Second, you have to customize the user input according to your criteria (8 or 11 digits)
TextFormatter is used to solve the problem. A UnaryOperator must be passed to it to filter user input only by numbers and StringConverter to validate user input.
This is an example implementation:
UnaryOperator<TextFormatter.Change> filter = change -> {
if(change.getControlNewText().matches("\\d{0,11}")) {
return change;
}
return null;
};
StringConverter<String> converter = new StringConverter<String>() {
#Override
public String toString(String s) {
if(s == null || s.isBlank()) return "";
if(s.matches("\\d{8}|\\d{11}")) {
return s;
}
return "";
}
#Override
public String fromString(String s) {
if(s == null || s.isBlank()) return "";
if(s.matches("\\d{8}|\\d{11}")) {
return s;
}
throw new RuntimeException("Converter error");
}
};
textField.setTextFormatter(new TextFormatter<>(converter, null, filter));

How do I print the content of a TextArea?

So currently I'm trying to make a print feature for my Notepad application. I already have a kind of working Print feature, but it prints the full TextArea not only the string that is written into it.
I already tried to make it just print a string, but the PrintJob is not able to handle it, because it needs the actual TextArea, where the Text is written into.
My current Print Stuff:
public void doPrint() {
String toPrint = writeArea.getText();
printSetup(writeArea, Main.primaryStage);
}
private final Label jobStatus = new Label();
private void printSetup(Node node, Stage owner)
{
// Create the PrinterJob
PrinterJob job = PrinterJob.createPrinterJob();
if (job == null)
{
return;
}
// Show the print setup dialog
boolean proceed = job.showPrintDialog(owner);
if (proceed)
{
print(job, node);
}
}
private void print(PrinterJob job, Node node)
{
// Set the Job Status Message
jobStatus.textProperty().bind(job.jobStatusProperty().asString());
// Print the node
boolean printed = job.printPage(node);
if (printed)
{
job.endJob();
}
}
What I want to have:
A print that only shows the String, just like any other notepad application does if you try to print something
What I currently get:
The full textarea with frame.
As I mentioned in the comment you can wrap it into a Text, but then the first line for some reason isn't displayed correctly.
The solution would be to use a Label instead like:
printSetup(new Label(toPrint), Main.primaryStage);

How to create a numeric localized (decimal separator) TextField?

I want to create a numeric TextField which will be localized.
For example, in Excel, if you are in english, if you type a number with the KeyPad, the decimal separator key will enter a dot '.'
But if you are in French, the decimal separator will be a comma ','
Therefore in Excel, the TextField is smart enough to detect in which Locale you're on, and to adapt the decimal separator you actually wanted to print in the TextField.
Right now in JavaFX, I haven't found a way to do that. I was thinking of listening to text modification and to replace any occurrence of dot with a comma if I detect I'm in French. But is this assumption true?
I would recommend subclassing TextField and overriding the insertText and replace* methods. This will handle text entry before the text is updated; listening to text modifications and fixing them will mean you temporarily have "invalid" text in your text field's text property, which could create problems in the rest of your application.
Note that you can create a java.text.DecimalFormatSymbols object for the default (user) Locale (or a specified Locale, if you need), and call getDecimalSeparator() to find the correct decimal separator character. (Similarly, you can call getGroupingSeparator().)
So, for proof of concept, this is nowhere near robust enough for production:
public class NumericTextField extends TextField {
#Override
public void insertText(int index, String text) {
String fixedText = fixText(text);
StringBuilder newText = new StringBuilder(getText().substring(0, index));
newText.append(fixedText);
if (index < getText().length()) {
newText.append(getText().substring(index));
}
if (isValid(newText)) {
super.insertText(index, fixedText);
}
}
#Override
public void replaceText(int start, int end, String text) {
String fixedText = fixText(text);
StringBuilder newText = new StringBuilder(getText().substring(0, start));
newText.append(fixedText);
newText.append(getText().substring(end));
if (isValid(newText)) {
super.insertText(start, end, fixedText);
}
}
#Override
public void replaceText(IndexRange range, String text) {
replaceText(range.getStart(), range.getEnd(), text);
}
private String fixText(String text) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
return text.replaceAll("\\.", Character.toString(symbols.getDecimalSeparator()));
}
private boolean isValid(String text) {
// check that the text is a valid numeric representation (or partial representation)
}
}
In the events of a TextField, "setOnKeyPressed" and "setOnKeyReleased", the code of the key pressed can be obtained from the keyEvent and there it can be seen that the key code of the "." pressed from the main keyboard corresponds to the constant "PERIOD", while the keystroke code of "." pressed from the numeric keypad corresponds to the constant "DECIMAL".
From this concept, I share the closest thing I have achieved in this regard, where if I press the "." From the numeric keyboard, according to my specific need, I replace it with the character ",".
totalAmountText.setOnKeyReleased((keyEvent) -> {
if (keyEvent.getCode() == DECIMAL) {
int pos = totalAmountText.getCaretPosition();
totalAmountText.setText(totalAmountText.getText().replace(".", ","));
totalAmountText.positionCaret(pos);
}
});
I am attaching a new version of the code, which takes best practices from the code of the first answer:
totalAmountText.setOnKeyReleased((keyEvent) -> {
if (keyEvent.getCode() == DECIMAL) {
int pos = totalAmountText.getCaretPosition();
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
totalAmountText.setText(totalAmountText.getText().replace(".",
Character.toString(symbols.getDecimalSeparator())));
totalAmountText.positionCaret(pos);
}
});

Resources