JavaFX Dynamic Form Field UI - javafx

Does anyone know how to imitate the functionality from the UI components shown below? I want to replicate adding form fields when text is entered into the TextField box. I don't need the dropdown button, just the dynamic adding of the forms.

You could modify the children of a GridPane adding a new TextField & Button every time one of the buttons is activated. Listen to the text properties to enable/disable the Button and save the results.
private static void insertRow(GridPane grid, List<String> values, int index) {
// increment index of children with rowIndex >= index
for (Node n : grid.getChildren()) {
int row = GridPane.getRowIndex(n);
if (row >= index) {
GridPane.setRowIndex(n, row + 1);
}
}
TextField text = new TextField();
Button add = new Button("+");
add.setDisable(true);
add.setOnAction(evt -> {
insertRow(grid, values, GridPane.getRowIndex(add) + 1);
});
values.add(index, "");
text.textProperty().addListener((a, oldValue, newValue) -> {
add.setDisable(newValue.isEmpty());
values.set(GridPane.getRowIndex(add), newValue);
});
grid.addRow(index, text, add);
}
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
List<String> list = new ArrayList<>();
insertRow(grid, list, 0);
Button print = new Button("print");
print.setOnAction(evt -> {
System.out.println(list);
});
grid.add(print, 0, 1);
Scene scene = new Scene(grid, 300, 500);
primaryStage.setScene(scene);
primaryStage.show();
}

This may not be exactly what you're looking for and may not be the best way to do this, but should be easy to adapt it to your needs.
Basically, you will need a list of HBox objects to be added to a VBox in your application. You could create the list yourself and bind it to the children of your VBox, or just add/remove the HBoxes to/from the VBox using the getChildren().add() and getChildren().remove() methods.
Here is a complete little application to demonstrate the concept. I created an internal class to handle the HBox with the fields you need. This could be adapted to be more felixable:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
private static VBox mainPane;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
mainPane = new VBox(5);
mainPane.setPadding(new Insets(10));
mainPane.setAlignment(Pos.TOP_CENTER);
mainPane.getChildren().add(new UIForms());
primaryStage.setScene(new Scene(mainPane));
primaryStage.show();
}
static void addField() {
mainPane.getChildren().add(new UIForms());
}
static void removeField(UIForms field) {
if (mainPane.getChildren().size() > 1) {
mainPane.getChildren().remove(field);
}
}
}
class UIForms extends HBox {
private TextField textField1;
private TextField textField2;
private Button btnAddField;
private Button btnRemoveField;
public UIForms() {
// Setup the HBox layout
setAlignment(Pos.CENTER_LEFT);
setSpacing(5);
// Create the UI controls
textField1 = new TextField();
textField2 = new TextField();
btnAddField = new Button("+");
btnRemoveField = new Button("-");
// Setup button actions
btnAddField.setOnAction(e -> Main.addField());
btnRemoveField.setOnAction(e -> Main.removeField(this));
// Add the UI controls
getChildren().addAll(
textField1, textField2, btnAddField, btnRemoveField
);
}
}

Related

changing my textfield to upper/lower case is not working as intended

After creating my GUI my next step was to have the buttons be able to change the textfield to their respective case when pressed. However none of the ways I have tried seem to demonstrate that.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class UpperLowerClass extends Application {
private Button upperButton;
private Button lowerButton;
private String userText;
private TextField userInput;
private Stage window;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Uppercase to Lowercase");
//GRID
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(10);
grid.setHgap(10);
//UPPERCASE BUTTON
upperButton = new Button("Uppercase");
upperButton.setOnAction(this::changeTextButton);
GridPane.setConstraints(upperButton, 0, 0);
//LOWERCASE BUTTON
lowerButton = new Button("Lowercase");
lowerButton.setOnAction(this::changeTextButton);
GridPane.setConstraints(lowerButton, 0, 1);
//TEXTFIELD
TextField userInput = new TextField();
userInput.getText();
userInput.setText(String.valueOf(userText));
GridPane.setConstraints(userInput, 0, 2);
grid.getChildren().addAll(upperButton, lowerButton, userInput);
//SCENE
Scene myScene = new Scene(grid, 300, 250);
window.setScene(myScene);
window.show();
primaryStage.setScene(myScene);
primaryStage.show();
}
//EVENT
public void changeTextButton(ActionEvent event)
{
if (event.getSource() ==upperButton) {
userText = userText.toUpperCase();
}
if (event.getSource() ==lowerButton) {
userInput.setText(String.valueOf(userText.toLowerCase()));
}
}
}
There must be a problem with how I am retrieving the text or is it entirely because of the manipulation I am trying to do in the If statements? Each button is the two versions I have tried which leads me to think that I am retrieving the text wrong or manipulating wrong. Thank you in advance!
Is the String userText needed elsewhere? Getting and setting the text of userInput directly through getText() and setText() would be better imo. Additionally, there is no need to create a local variable for userInput when you have it as a global variable already.
This is your code tested working: (I removed the changeTextButton method and just made the changes from the buttons directly, but this could be split out easily enough.)
public class UpperLowerClass extends Application {
private Button upperButton;
private Button lowerButton;
private TextField userInput;
private Stage window;
#Override
public void start(Stage primaryStage) {
try {
window = primaryStage;
//GRID
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(10);
grid.setHgap(10);
//UPPERCASE BUTTON
upperButton = new Button("Uppercase");
upperButton.setOnAction(event -> {
if(userInput.getText()!=null) {
userInput.setText(userInput.getText().toUpperCase());
}
});
// upperButton.setOnAction(this::changeTextButton);
GridPane.setConstraints(upperButton, 0, 0);
//LOWERCASE BUTTON
lowerButton = new Button("Lowercase");
//lowerButton.setOnAction(this::changeTextButton);
lowerButton.setOnAction(event -> {
if(userInput.getText()!=null) {
userInput.setText(userInput.getText().toLowerCase());
}
});
GridPane.setConstraints(lowerButton, 0, 1);
//TEXTFIELD
userInput = new TextField(); //don't need to create new Textfield here
GridPane.setConstraints(userInput, 0, 2);
grid.getChildren().addAll(upperButton, lowerButton, userInput);
//SCENE
Scene myScene = new Scene(grid, 300, 250);
window.setScene(myScene);
window.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

JavaFx property binding with multiple objects on on screen

I use JavaFx with property binding.
I got a object 'Person' with the properties 'name' and age.
These objects are stored in a ObservableList.
The properties are bound to labels on the gui. When I change the person in the ListBox the data also change on the right hand side.
GUI with person list:
And now it comes to my problem.
I want to disply all persons on one window, like the next picture shows.
GUI with multiple persons on one view:
How can I handle this. I thought about HBox but the binding doesn't work.
FYI: Here you can find the tutorial I used.
https://code.makery.ch/library/javafx-tutorial/part1/
This looks like a perfect time to use a ListView with custom ListCell implementations.
The sample application below shows a very basic application that displays each Person object in a ListView. We will provide our own ListCell so we can control exactly how each Person gets displayed.
I also added a profile photo just for fun :)
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewDetailSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// First, let's create our list of Persons
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.addAll(
new Person("John", 34),
new Person("Cheyenne", 24),
new Person("Micah", 17),
new Person("Katelyn", 28)
);
// Create a ListView
ListView<Person> listView = new ListView<>();
// Bind our list to the ListView
listView.setItems(persons);
// Now, for the magic. We'll create our own ListCells for the ListView. This allows us to create a custom
// layout for each individual cell. For this sample, we'll include a profile picture, the name, and the age.
listView.setCellFactory(new Callback<ListView<Person>, ListCell<Person>>() {
#Override
public ListCell<Person> call(ListView<Person> param) {
return new ListCell<Person>() {
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
// Set any empty cells to show nothing
if (person == null || empty) {
setText(null);
setGraphic(null);
} else {
// Here we can build our layout. We'll use a HBox for our root container
HBox cellRoot = new HBox(5);
cellRoot.setAlignment(Pos.CENTER_LEFT);
cellRoot.setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/sample/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
cellRoot.getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
cellRoot.getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the cellRoot
cellRoot.getChildren().add(vBox);
// Finally, set this cell to display our custom layout
setGraphic(cellRoot);
}
}
};
}
});
// Now, add our ListView to the root layout
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(450);
primaryStage.setHeight(400);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
// Simple Person class
class Person {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty age = new SimpleIntegerProperty();
public Person(String name, int age) {
this.name.set(name);
this.age.set(age);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getAge() {
return age.get();
}
public IntegerProperty ageProperty() {
return age;
}
public void setAge(int age) {
this.age.set(age);
}
}
The Result:
Without ListView:
If you'd prefer not to use a ListView for this display, you can keep another list of your Person displays and bind that to the children list of whichever container you want:
// Create a list to hold our individual Person displays
ObservableList<Node> personDisplays = FXCollections.observableArrayList();
// Now add a new PersonDisplay to the list for each Person in the personsList
persons.forEach(person -> personDisplays.add(new PersonDisplay(person)));
// Bind our personsDisplay list to the children of our root VBox
Bindings.bindContent(root.getChildren(), personDisplays);
PersonDisplay class:
class PersonDisplay extends HBox {
public PersonDisplay(Person person) {
// First, let's configure our root layout
setSpacing(5);
setAlignment(Pos.CENTER_LEFT);
setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the layout
getChildren().add(vBox);
}
}
The Result:

How to find the ListCell which is at the location of the drop position in javafx

I have a ListView which is a dragdrop target. When handling OnDragDropped event, I would like to find the list cell which is at the position of the mouse. In addition I want to highlight items when mouse is hovered above them even during a drag drop operation. How can this be achieved in JavaFx.
Use a cell factory on the list view to define custom cells: that way you can register the drag handlers with the individual cells, instead of with the list view. Then it is easy for the drag handlers to know which cells have fired the event.
Here is a SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class ListViewWithDragAndDrop extends Application {
#Override
public void start(Stage primaryStage) {
ListView<String> listView = new ListView<>();
listView.getItems().addAll("One", "Two", "Three", "Four");
listView.setCellFactory(lv -> {
ListCell<String> cell = new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty) ;
setText(item);
}
};
cell.setOnDragOver(e -> {
Dragboard db = e.getDragboard();
if (db.hasString()) {
e.acceptTransferModes(TransferMode.COPY);
}
});
cell.setOnDragDropped(e -> {
Dragboard db = e.getDragboard();
if (db.hasString()) {
String data = db.getString();
if (cell.isEmpty()) {
System.out.println("Drop on empty cell: append data");
listView.getItems().add(data);
} else {
System.out.println("Drop on "+cell.getItem()+": replace data");
int index = cell.getIndex();
listView.getItems().set(index, data);
}
e.setDropCompleted(true);
}
});
// highlight cells when drag target. In real life, use an external CSS file
// and CSS pseudoclasses....
cell.setOnDragEntered(e -> cell.setStyle("-fx-background-color: gold;"));
cell.setOnDragExited(e -> cell.setStyle(""));
return cell ;
});
TextField textField = new TextField();
textField.setPromptText("Type text and drag to list view");
textField.setOnDragDetected(e -> {
Dragboard db = textField.startDragAndDrop(TransferMode.COPY);
String data = textField.getText();
Text text = new Text(data);
db.setDragView(text.snapshot(null, null));
ClipboardContent cc = new ClipboardContent();
cc.putString(data);
db.setContent(cc);
});
BorderPane root = new BorderPane(listView, textField, null, null, null);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX: how to hide the item selection after having selected an item

I made a WebEngine where i can use favorite Web URLS from a Choicebox (= favBox).
After having chosen an item, the item is shown and the website is loading. But my problem is: the item can still be seen for the rest of the session. How can I hide the item selection and just show the ChoiceBox without items?
Thanks a lot
#FXML
private void handleFavoritLoading(MouseEvent event) {
//favBox is a ChoiceBox
favBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
System.out.println("OK");
browser.load(favBox.getItems().get(newValue.intValue()));
// how to make the favBox not showing the selected item???
}
});
}
You can clear the selection of a ChoiceBox, then nothing will be selected in it.
favBox.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue != null) {
browser.load(newValue);
favBox.getSelectionModel().clearSelection();
}
}
);
Note that this behavior is a little bit strange as most of the time you probably want the selected choice to continue to be shown after selection. However, if you don't want the standard operation and want to immediately clear the choice after selection, you can always use the sample code provided here.
Sample app:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.Stage;
import static javafx.collections.FXCollections.observableArrayList;
public class HiddenChoices extends Application {
#Override
public void start(Stage stage) throws Exception {
WebView webView = new WebView();
WebEngine browser = webView.getEngine();
VBox.setVgrow(webView, Priority.ALWAYS);
ChoiceBox<String> favBox = new ChoiceBox<>(
observableArrayList(
"http://www.google.com",
"http://andrew-hoyer.com/experiments/cloth/",
"http://www.effectgames.com/demos/canvascycle/",
"http://www.zynaps.com/site/experiments/environment.html?mesh=bart.wft"
)
);
favBox.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue != null) {
browser.load(newValue);
favBox.getSelectionModel().clearSelection();
}
}
);
ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(browser.getLoadWorker().progressProperty());
progress.visibleProperty().bind(browser.getLoadWorker().runningProperty());
HBox controls = new HBox(10, favBox, progress);
controls.setMinHeight(HBox.USE_PREF_SIZE);
controls.setAlignment(Pos.CENTER_LEFT);
stage.setScene(
new Scene(
new VBox(10, controls, webView)
)
);
stage.show();
favBox.getSelectionModel().select(0);
}
public static void main(String[] args) {
Application.launch();
}
}

javafx multiple buttons to same handler

I try to make a simple calculator with 20 buttons and one handler. In java I can use 'if' statement with event.getSource() in ActionPerformed to check which button is pressed, but it doesn't work with handler in javafx. Is it possible in javafx that all buttons has one handler? (I don't want to use java 8 Lambdas.)
Last time I tried with setId/getId but it same not work (to me).
public class Calculator extends Application {
public Button b0, b1;
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
b0 = new Button("0");
b0.setId("0");
b0.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
grid.add(b0, 0, 1);
b0.setOnAction(myHandler);
b1 = new Button("1");
b1.setId("1");
b1.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
grid.add(b1, 0, 0);
b1.setOnAction(myHandler);
Scene scene = new Scene(grid, 365, 300);
scene.getStylesheets().add
(Calculator.class.getResource("calculator.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
final EventHandler<ActionEvent> myHandler = new EventHandler<ActionEvent>(){
#Override
public void handle(final ActionEvent event) {
Button x = (Button) event.getSource();
if (x.getId().equals(b0.getId()))
System.out.println("0");
else if(x.getId().equals(b1.getId()))
System.out.println("1");
}
};
public static void main(String[] args) {
launch(args);
}
}
I tested your code and it seems to work just fine.
There's no real reason to test the ids of the buttons, though. If you really want to use the same handler (which I don't advise), just test for equality between each button and the source of the event:
final EventHandler<ActionEvent> myHandler = new EventHandler<ActionEvent>(){
#Override
public void handle(final ActionEvent event) {
if (event.getSource() == b0)
System.out.println("0");
else if(event.getSource() == b1)
System.out.println("1");
}
};
But it's (almost?) always better to use a different handler for each action. It keeps the code free of all the if/else constructs, which both makes it cleaner and better in terms of performance. Here, since your buttons do almost the same thing, you can use a single implementation but multiple objects.
Here's a complete example:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class Calculator extends Application {
private final IntegerProperty value = new SimpleIntegerProperty();
class NumberButtonHandler implements EventHandler<ActionEvent> {
private final int number ;
NumberButtonHandler(int number) {
this.number = number ;
}
#Override
public void handle(ActionEvent event) {
value.set(value.get() * 10 + number);
}
}
#Override
public void start(Stage primaryStage) {
GridPane grid = createGrid();
for (int n = 1; n<10; n++) {
Button button = createNumberButton(n);
int row = (n-1) / 3;
int col = (n-1) % 3 ;
grid.add(button, col, 2 - row);
}
Button zeroButton = createNumberButton(0);
grid.add(zeroButton, 1, 3);
Button clearButton = createButton("C");
// without lambdas:
// clearButton.setOnAction(
// new EventHandler<ActionEvent>() {
// #Override
// public void handle(ActionEvent event) {
// value.set(0);
// }
// }
// );
// with lambdas:
clearButton.setOnAction(event -> value.set(0));
grid.add(clearButton, 2, 3);
TextField displayField = createDisplayField();
BorderPane root = new BorderPane();
root.setPadding(new Insets(10));
root.setTop(displayField);
root.setCenter(grid);
Scene scene = new Scene(root, 365, 300);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
private Button createNumberButton(int number) {
Button button = createButton(Integer.toString(number));
button.setOnAction(new NumberButtonHandler(number));
return button ;
}
private Button createButton(String text) {
Button button = new Button(text);
button.setMaxSize(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
GridPane.setFillHeight(button, true);
GridPane.setFillWidth(button, true);
GridPane.setHgrow(button, Priority.ALWAYS);
GridPane.setVgrow(button, Priority.ALWAYS);
return button ;
}
private GridPane createGrid() {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(10));
return grid;
}
private TextField createDisplayField() {
TextField displayField = new TextField();
displayField.textProperty().bind(Bindings.format("%d", value));
displayField.setEditable(false);
displayField.setAlignment(Pos.CENTER_RIGHT);
return displayField;
}
public static void main(String[] args) {
launch(args);
}
}

Resources