Restrict user to give input up to 3 characters in javafx - javafx

I am new to javaFx so please ignore my silly question here I want to validate (restrict) user to give 3 characters input (if user put more input, it should not allowed or after 3 characters, no latter should be visible) I found many solutions for validation but it does not restricting up to 3 characters as well as it little confusing to understand Here is my code.
public class editController {
#FXML
private TextField countrycode;
public void add(ActionEvent event) {
String ADD=countrycode.getText();
try {
if(ADD.isEmpty()){
Alert alert=new Alert(Alert.AlertType.ERROR);
alert.setHeaderText(null);
alert.setContentText("Please Fill All DATA");
alert.showAndWait();
return;
}
FXMLLoader loader =new FXMLLoader();
loader.load(getClass().getResource("/region/newCountry.fxml").openStream());

Validate using textProperty Listener like:
countrycode.textProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.length() > 3) {
countrycode.setText(oldValue);
}
}
});
Or get a String length and check that like
int length = ADD.length();
if (length > 3) {
System.out.println("Please Enter Lessthen 3 character");
}

you have to #Override the replaceText(int,int,String) method of your textField this way:
#Override
public void replaceText(int start, int end, String text) {
if ("".equals(text)) { // handles delete
super.replaceText(start, end, text);
return;
}
if (getText().length() < 3) { // handles add and checks length.
super.replaceText(start, end, text);
}
}
This won't let you to insert more than 3 characters, but you can define any regexp and use instead of the second if condition.

The solution of #KeyurBhanderi is good enough until you are typing char by char. But when you try to paste from the clipboard a long text you will not see any changes in the text field. Surprise!
As alternative to avoid confusing set at least truncated string:
field.setText(newValue.substring(0, maxLimit));
In this case the end user will see a part of text and will quickly understand what is going on. Even better to show a popup window with a warning.

Related

How to set KeyEvent to KeyCode.BACK_SAPCE in JavaFx?

I'm trying to set KeyEvent KeyCode to KeyCode.BACK_SAPCE if entered value is not a digit.
But I'm not able to achieve it
public void textFieldKeyReleased(KeyEvent e) {
if (!e.getCode().isDigitKey()) {
textField.setText(""); //manually set text
e.getCode() = KeyCode.BACK_SPACE; //required: variable found: value
}
}
I want to remove a character if it is not a digit.
I've assigned KeyCode.BACK_SAPCE to KeyEvent e but doesn't work.
Hot to use KeyCode (s)?
I manually set textField.setText(""); but want to make use of KeyCode
So you want to prevent any non-digit chars from being added to the TextField? There is actually a much better way of doing this: use a TextFormatter that prevents any changes that result in text that isn't desired. This works much better with copy&paste ect.. You can even implement logic for fixing a change, e.g. removing any non-digit chars from in case of copy&paste.
#Override
public void start(Stage primaryStage) throws Exception {
TextField digitsOnly = new TextField();
TextFormatter formatter = new TextFormatter((UnaryOperator<TextFormatter.Change>) change -> {
if (change.getControlNewText().matches("\\d*")) {
return change; // allow change without modifying it
} else {
return null; // don't allow change
}
});
digitsOnly.setTextFormatter(formatter);
Scene scene = new Scene(new VBox(digitsOnly));
primaryStage.setScene(scene);
primaryStage.show();
}

How to set scroll in the way that a certain part of the text is visible JavaFX

Good Evening, Everybody!
There is an object called TextArea, and I use it to display text,big text of a big book, making it not Editable. If it's not able to display all the text a the same time, it gains a scroll and displays only a part of itself.
So that's the point, can we set which exactly part of the text, shall be displayed?
Just a kleiner hint, I don't ask more)
P.S. Also, Fisrt, I asked myself a question, what to use to display required text, and didn't find anything better than TextArea, probably, because of a bad seeking. Maybe, someone of you, dear programmers, came across the same problem and found a better solution?
You do not have to show everything, you can display number of lines(from big string), and by events(like Mouse wheel) push next line of the string to your TextArea
simple Example:
public class Controller {
public TextArea text;
int current=0;
int d=1;
int rowSize= 20;
int rowsToSee=10;
String[] strings=null;
#FXML
public void initialize() {
String string ="";
for(int i=0;i<3000;i++){
string=string+" "+i;
if(i%rowSize==0){
string=string+"\n";
}
}
strings = string.split("\n");
final String s=string;
text.setText(returnLines(current,current+rowsToSee,strings));
text.setOnScroll(new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
if(event.getDeltaY()<0){
text.setText(returnLines(current,current+rowsToSee,strings));
current=current+d;
}else {
if(current!=0)
{ current=current-d;
text.setText(returnLines(current,current+rowsToSee,strings));
}
}}
});
}
String returnLines(int from,int to,String[] strArry){
String s="";
for(int i=from;i<to;i++){
if(strArry.length>i){
s=s+strArry[i]+"\n";
}
}
return s;
}
}

How to make Hyperlink TableColumn in TableView editable

TableColumn<ComponentObject, Hyperlink> template_id = new TableColumn<ComponentObject, Hyperlink>("Template Id");
template_id.setCellValueFactory(
new PropertyValueFactory<ComponentObject, Hyperlink>("template"));
template_id.setCellFactory(TextFieldTableCell.forTableColumn());
template_id.setOnEditCommit(
new EventHandler<CellEditEvent<ComponentObject, Hyperlink>>() {
#Override
public void handle(CellEditEvent<ComponentObject, Hyperlink> t) {
((ComponentObject) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setTemplate((javafx.scene.control.Hyperlink) t.getNewValue());
}
}
);
Here setCellFactory is giving error as
"
The method setCellFactory(Callback<TableColumn<ComponentObject,Hyperlink>,TableCell<ComponentObject,Hyperlink>>) in
the type TableColumn<ComponentObject,Hyperlink> is not applicable for the arguments
(Callback<TableColumn<Object,String>,TableCell<Object,String>>)
"
How to solve this? I want to make template_id column editable..but note that it is hyperlink..
The quick and dirty way is to specify a stringConverter for the TextFieldTableCell:
StringConverter<Hyperlink> converter = new StringConverter<Hyperlink>() {
#Override
public Hyperlink fromString(String string) {
return new Hyperlink(string);
}
#Override
public String toString(Hyperlink hyperlink) {
return hyperlink.getText();
}
}
template_id.setCellFactory(TextFieldTableCell.forTableColumn(converter));
In general, though, it is a very bad idea to represent your data with UI nodes, which is what you are doing by making the column type a Hyperlink. Strange things may happen here. It would be better to make the data type String, and to implement your own TableCell that showed a Hyperlink when not in editing mode, and a TextField when in editing mode.

JavaFX: Run ChangeListener

Ho can I run the change method inside ChangeListener in the initialization.
Since it only run when changes happen. For example I have a TextField that I want to set it's value to 0 when it's empty, and I do it like this:
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.isEmpty())
textField.setText("0");
}
});
But at the start of the application the changed method is not called. So how to work around this issue?
There's no way to access a listener added to a property. Obviously you could just keep a reference to it:
ChangeListener<String> textFieldListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.isEmpty())
textField.setText("0");
};
textField.textProperty().addListener(textFieldListener);
textFieldListener.changed(null, null, textField.getText());
or, perhaps more naturally, just move the actual functionality to a different method:
textField.textProperty().addListener((observable, oldValue, newValue) -> vetoTextFieldChanges());
vetoTextFieldChanges();
// ...
private void vetoTextFieldChanges() {
String newText = textField.getText();
if (!newText.matches("\\d*")) {
textField.setText(newText.replaceAll("[^\\d]", ""));
}
if (newText.isEmpty())
textField.setText("0");
}
Note that the whole approach of watching for changes and then modifying them if they are inconsistent with your business logic is not very satisfactory. For example, if you have other listeners registered with the property, they may see the intermediate (invalid) values of the text. The supported way to do this is to use a TextFormatter. The TextFormatter both allows you to veto changes that are requested to the text, before the textProperty() is updated, and to convert the text to an appropriate value. So in your case you could do:
UnaryOperator<TextFormatter.Change> filter = change -> {
// remove any non-digit characters from inserted text:
if (! change.getText().matches("\\d*")) {
change.setText(change.getText().replaceAll("[^\\d]", ""));
}
// if new text is empty, replace all text with "0":
if (change.getControlNewText().isEmpty()) {
change.setRange(0, change.getControlText().length());
change.setText("0");
}
return change ;
};
TextFormatter<Integer> formatter = new TextFormatter<Integer>(new IntegerStringConverter(), 0, filter);
textField.setTextFormatter(formatter);
Now you can use the formatter to get the (numeric) values directly via the formatter's value property, and bind them to your model, etc:
// assume model is a data model and valueProperty() returns a Property<Integer>:
formatter.valueProperty().bindBidirectional(model.valueProperty());
Note that a binding like this will both update the formatter (and consequently the text field) when the model changes, and will also initialize it depending on your model value (thus providing a solution for your original question).

How do I automatically trigger enter key in Javafx

Nowadays I am working on raspberry pi and I write some programs in java , javafx platforms.I just would like to inform you that I am simply beginner on javafx.
According to that I just would like to trigger ENTER key after changing my textfield.Working principle of my program is like this;
1)I have created one masterform fxml and it is directing all other pages with one textfield.
2)I created main method that let me to use keyboard to enter some specific String values to assign them to textfield for page alteration.
3)I have a bridge java page, it includes global variables to use everywhere in project.So Firstly I set value from keyboard to these global variables.These global variables are created as stringproperty for adding actionlistener for any change.
4)Then I set these global variables to textfield.
5)Textfield indicates relevant values from keyboard.But Unfortunately I can not forward the pages without pressing to enter key.In this case ı would like to trigger this textfield.But unfortunately ı have no idea how to trigger texfield without pressing enter key.Therefore I decided to make auto trigger to enter key for this textfield.
I simply used robot method;
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ENTER);
But it didn't work.Because After I set the global variable to textfield for first time.It does not define the value of the textfield is changed.It determines after pressing the enter key.
So how can I trigger this textfield after getting value of my global variables.I would like to pass how to set pages, I will show you how my program works.
Example of my code is;
Main method
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
for (String strBarcode = scanner.nextLine(); !strBarcode.isEmpty();
strBarcode = scanner.nextLine()) {
if (strBarcode.equals("distribution")){
Global.G_MOD.set("distribution");
System.out.println(Global.G_MOD.get());
}
}}
GlobalVariables.java(bridge page)
public class Global{
public static StringProperty G_MOD = new SimpleStringProperty("");
}
My MasterController Page for javafx
public class masterformController implements Initializable {
#FXML
public TextField tbxBarcode;
#FXML
void onchangetbxBarcode(ActionEvent event) {
if(Global.G_MOD.get().equals("distribution")){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/puttolightfx/fxml/page1.fxml"));
Parent rootpage1 = (Parent)loader.load();
pnPages.getChildren().clear();
pnPages.getChildren().add(rootpage1);
} catch (IOException ex) {
Logger.getLogger(masterformController.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Global.G_MOD.addListener(new ChangeListener(){
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
String Newvalue = (String)newValue;
tbxBarcode.setText(Global.G_MOD.get());}
});
}
}
So Everything is working, just I have to trigger textfield when the global value : Global.G_MOD is indicated on texfield.Then it will pass to another page according to global value of Global.G_MOD : "distribution".
SOLUTION(SOLVED):
I solved my problem using thread on listener of the textfield.I gave up to trigger enter key automatically and focused on textfield change.
I simply decided to use thread to change .fxml pages in textfield listener.
Platform.runLater(new Runnable() {
#Override
public void run() {
//if you change the UI, do it here !
}
});
EDITED CODE :
tbxBarcode.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
String Newvalue=(String)newValue;
System.out.println(tbxBarcode.getText());
Platform.runLater(new Runnable() {
#Override
public void run() {
if(Global.G_MOD.get().equals("distribution")){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/puttolightfx/fxml/page1.fxml"));
Parent rootpage1 = (Parent)loader.load();
pnPages.getChildren().clear();
pnPages.getChildren().add(rootpage1);
} catch (IOException ex) {
Logger.getLogger(masterformController.class.getName()).log(Level.SEVERE, null, ex);
}
}
// }
}
});
});
Try using
textField.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ENTER, true, true, true, true));
According to the docs
public KeyEvent(EventType<KeyEvent> eventType,
String character,
String text,
KeyCode code,
boolean shiftDown,
boolean controlDown,
boolean altDown,
boolean metaDown)
Constructs new KeyEvent event with null source and target and KeyCode object directly specified.
Parameters:
eventType - The type of the event.
character - The character or sequence of characters associated with the event
text - A String describing the key code
code - The integer key code
shiftDown - true if shift modifier was pressed.
controlDown - true if control modifier was pressed.
altDown - true if alt modifier was pressed.
metaDown - true if meta modifier was pressed.
Since:
JavaFX 8.0
You can refer https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/KeyEvent.html
Edit 1
You need to identify the moment when Enter key event must be triggered.
For example:
If your textfield allows a limited number of characters, then you can add the above mentioned code in the following way:
txtField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.length()>30) {
txtField.setText(oldValue);
txtField.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ENTER, true, true, true, true));
}
}
});
This is just an example. It can fire your event multiple times, so you need to write the code to fire the event just once.

Resources