I'm looking to format a phone number in real time as it is typed into a TextField. The goal is to add a "separator" (a space, a dash, a dot...) between the different digits forming the phone number according to the country. By default France : 06 23 65 14 85 But also for other countries as the international format French : +33 6 23 65 14 85 Or the german international format : +xx xxx xxx xx xxx
For this, I have a Listener that looks permanently when a new number is added in the TextField. The program then takes care of detecting the format of the phone number that is being entered, and depending on that, the program uses a "format" that I created myself to modify the format of the string.
The problem is that the user's cursor is constantly moving while reformatting the string. If the user deletes or selects a zone and then deletes it... each time, the cursor moves to the beginning or the end of the string which is very annoying to write.
Would you have a solution to change the format of the string in real time while making sure that the cursor is in the right place? The only alternative I found for the moment is a lib but that seems to be difficult to maintain so I'd rather do it myself. Otherwise I have to wait for the unfocus on the graphic component and change the format after that, but that's not what I want.
I provide you my code below, excuse me if there is a little bit of French in it, I sometimes write some.
import java.util.function.UnaryOperator;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.util.converter.IntegerStringConverter;
public class FormPhoneNumber {
private StringProperty textPropertyFromComponent = new SimpleStringProperty();
private TextField tf;
private String separator;
private String last = "";
private int lastCaretPosition = 0;
private ChangeListener<String> updateText;
public FormPhoneNumber(TextField textField, String separator) {
this.textPropertyFromComponent = textField.textProperty();
this.tf = textField;
//On unfocus
// tf.focusedProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
// if (t1 == false) {
// System.out.println("i");
// }
// });
if (separator != null && !separator.isEmpty()) {
this.separator = separator;
} else {
separator = " ";
}
checkNumberFormat();
}
private void checkNumberFormat() {
//Listener on write in the textfield
updateText = (ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
if (newValue == null || newValue.isEmpty()) {
return;
}
//My customs formatters
if (newValue.startsWith("+33")) {
// System.out.println("FRANCE");
String resultat = getPhoneNumberFormatted(newValue, "###-#-##-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 17);
} else if (newValue.startsWith("+49")) {
// System.out.println("ALLEMAGNE");
String resultat = getPhoneNumberFormatted(newValue, "###-###-###-##-###");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 18);
} else if (newValue.startsWith("+34")) {
// System.out.println("ESPAGNE");
String resultat = getPhoneNumberFormatted(newValue, "###-###-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.replaceAll(separator, "").startsWith("+3245") || newValue.replaceAll(separator, "").startsWith("+3249")) {
// System.out.println("BELGIQUE GSM");
String resultat = getPhoneNumberFormatted(newValue, "###-###-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.replaceAll(separator, "").startsWith("+322") || newValue.replaceAll(separator, "").startsWith("+323") || newValue.replaceAll(separator, "").startsWith("+329")) {
// System.out.println("BELGIQUE AUTRE 1");
String resultat = getPhoneNumberFormatted(newValue, "###-#-###-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 15);
} else if (newValue.startsWith("+32")) {
// System.out.println("BELGIQUE");
String resultat = getPhoneNumberFormatted(newValue, "###-##-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 15);
} else if (newValue.replaceAll(separator, "").startsWith("+3526")) {
// System.out.println("LUXEMBOURG 1");
String resultat = getPhoneNumberFormatted(newValue, "####-###-###-###");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.startsWith("+352")) {
// System.out.println("LUXEMBOURG AUTRE");
String resultat = getPhoneNumberFormatted(newValue, "####-##-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.startsWith("+377")) {
// System.out.println("MONACO");
String resultat = getPhoneNumberFormatted(newValue, "####-##-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.startsWith("+41")) {
// System.out.println("SUISSE");
String resultat = getPhoneNumberFormatted(newValue, "###-##-###-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.replaceAll(separator, "").startsWith("+390")) {
// System.out.println("ITALIE FIXE");
String resultat = getPhoneNumberFormatted(newValue, "###-##-####-####");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.replaceAll(separator, "").startsWith("+393")) {
// System.out.println("ITALIE PORTABLE");
String resultat = getPhoneNumberFormatted(newValue, "###-###-###-####");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.startsWith("+39")) {
// System.out.println("ITALIE AUTRE");
String resultat = getPhoneNumberFormatted(newValue, "###-##-####-####");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.startsWith("+376")) {
// System.out.println("ANDORRE");
String resultat = getPhoneNumberFormatted(newValue, "####-###-###-###");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 16);
} else if (newValue.startsWith("+590")) {
// System.out.println("GUADELOUPE");
String resultat = getPhoneNumberFormatted(newValue, "####-###-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 17);
} else if (newValue.startsWith("+596")) {
// System.out.println("MARTINIQUE");
String resultat = getPhoneNumberFormatted(newValue, "####-###-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 17);
} else if (newValue.startsWith("+594")) {
// System.out.println("GUYANE");
String resultat = getPhoneNumberFormatted(newValue, "####-###-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 17);
} else if (newValue.startsWith("+262")) {
// System.out.println("REUNION ET MAYOTTE");
String resultat = getPhoneNumberFormatted(newValue, "####-###-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 17);
} else if (newValue.startsWith("+687")) {
// System.out.println("NOUVELLE-CALEDONIE");
String resultat = getPhoneNumberFormatted(newValue, "####-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 13);
} else if (newValue.startsWith("+")) {
// System.out.println("AUTRE - PAYS NON REFERENCE");
String resultat = getPhoneNumberFormatted(newValue, "######################");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 22);
} else if (newValue.length() >= 3) {
if (Character.isDigit(newValue.charAt(0))) {
// System.out.println("France Nationnal");
String resultat = getPhoneNumberFormatted(newValue, "##-##-##-##-##");
int caretPosition = tf.getCaretPosition();
setResult(resultat, caretPosition, oldValue, newValue, 14);
}
}
};
textPropertyFromComponent.addListener(updateText);
}
//My attempt to put the cursor in the right place, but VERY scary code
private void setResult(String resultat, int caretPosition, String oldValue, String newValue, int maxLength) {
Platform.runLater(() -> {
if (resultat.length() <= maxLength && newValue != null && oldValue != null) {
textPropertyFromComponent.set(resultat);
// int diffAddLetter = newValue.length() - oldValue.length();
// int diffRemLetter = oldValue.length() - newValue.length();
// System.out.println("oldValue = " + oldValue);
// System.out.println("newValue = " + newValue);
// System.out.println("resultat = " + resultat);
// System.out.println("test = " + last);
// if (newValue.length() == maxLength) {
// if (!"".equals(last)) {
// tf.positionCaret(caretPosition + 1);
// }
// } else if (diffAddLetter > 0) {
// System.out.println("diffAddLetter = " + diffAddLetter);
// System.out.println("lastCaretPosition = " + lastCaretPosition);
// System.out.println("caretPosition = " + caretPosition);
// if ("".equals(last)) {
// if (StringUtils.countMatches(oldValue, separator) < StringUtils.countMatches(newValue, separator)) {
// tf.positionCaret(caretPosition + 1);
// } else {
// tf.positionCaret(caretPosition);
// }
// System.out.println("a1");
// } else {
// tf.positionCaret(caretPosition + diffAddLetter);
// System.out.println("a2");
// }
// if (lastCaretPosition == caretPosition) {
// last = "";
// } else {
// last = oldValue;
// }
// } else if (diffRemLetter > 0) {
// System.out.println("lastCaretPosition = " + lastCaretPosition);
// System.out.println("caretPosition = " + caretPosition);
// System.out.println("diffRemLetter = " + diffRemLetter);
// if ((lastCaretPosition == caretPosition || lastCaretPosition == (caretPosition - diffRemLetter)) && diffRemLetter > 1) {
// tf.positionCaret(caretPosition - diffRemLetter);
// System.out.println("r1");
// } else {
// System.out.println("caretPosition + diffRemLetter = " + (caretPosition - diffRemLetter));
//// System.out.println("(newValue.substring(0, caretPosition) = " + (newValue.substring(0, caretPosition)));
// System.out.println("oldValue.substring(0, caretPosition) = " + oldValue.substring(0, caretPosition));
//// System.out.println("newValue.substring(caretPosition) = " + newValue.substring(caretPosition +1));
// System.out.println("caretPosition = " + caretPosition);
// if (newValue.length() > caretPosition) {
// if (newValue.substring(0, caretPosition).equals(oldValue.substring(0, caretPosition)) && oldValue.endsWith(newValue.substring(caretPosition + 1)) && !"".equals(newValue.substring(caretPosition + 1)) && !String.valueOf(newValue.charAt(caretPosition - 1)).equals(separator)) {
// tf.positionCaret(caretPosition);
// System.out.println("r2");
// } else if (Character.isDigit(newValue.charAt(caretPosition - 1)) && Character.isDigit(newValue.charAt(caretPosition)) && diffRemLetter == 1) {
// tf.positionCaret(caretPosition);
// System.out.println("r5");
// } else {
// tf.positionCaret(caretPosition + 1);
// System.out.println("r3");
// }
// } else {
// System.out.println("r4");
// tf.positionCaret(lastCaretPosition);
// }
// last = oldValue;
// }
// } else if (String.valueOf(resultat.charAt(caretPosition - 1)).equals(separator) && last.length() < resultat.length()) {
// tf.positionCaret(caretPosition + 1);
// System.out.println("s");
// } else {
// tf.positionCaret(caretPosition);
// System.out.println("o");
// }
// if (resultat.length() >= caretPosition) {
// if (oldValue.replaceAll(separator, "").equals(newValue.replaceAll(separator, "")) && String.valueOf(resultat.charAt(caretPosition)).equals(separator)) {
// tf.positionCaret(caretPosition);
// System.out.println("c");
// }
// }
// lastCaretPosition = caretPosition;
} else {
textPropertyFromComponent.set(oldValue);
}
});
textPropertyFromComponent.addListener(updateText);
}
//Method to format the phone number
private String getPhoneNumberFormatted(String phoneNumber, String format) {
textPropertyFromComponent.removeListener(updateText);
String vretour = "";
if (phoneNumber.length() <= format.length()) {
phoneNumber = phoneNumber.replaceAll("[^0-9+]", "");
int index = findXDotPosition(format, phoneNumber.length());
format = format.substring(0, index + 1);
int indexCharArray = 0;
for (char unChar : format.toCharArray()) {
if ("#".charAt(0) == unChar) {
vretour = vretour + phoneNumber.toCharArray()[indexCharArray];
indexCharArray++;
}
if ("-".charAt(0) == unChar) {
vretour = vretour + separator;
}
}
} else {
vretour = phoneNumber;
}
return vretour;
}
//Method to find a separator
private static int findXDotPosition(String s, int separatorToFind) {
int result = -1;
char[] ca = s.toCharArray();
for (int i = 0; i < ca.length; ++i) {
if (ca[i] == '#') {
--separatorToFind;
}
if (separatorToFind == 0) {
return i;
}
}
return result;
}
}
I also provide you some screenshots of the actual working of the application.
https://i.goopics.net/Jl8aR.png https://i.goopics.net/RVvdZ.png
Hoping you can offer me a solution, Thanks in advance !