I'm working on a very small, brief application to calculate charges for an upcoming conference. The app displayed fine when I ran the code until I added my event handler. Everything seems to be in check so I am unsure of what is happening. Any insight would be much appreciated.
//JavaFX imports
import javafx.application.Application;
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.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ConferenceRegistration extends Application {
// Create radio buttons for conference event options
RadioButton generalAdmissionButton, studentAdmissionButton, keynoteDinnerButton, eCommerceButton, webFutureButton,
advancedJavaButton, securityButton;
Label labelAdmission, labelOptionalEvents, totalChargesLabel;
Button totalCharges;
public static void main(String[] args) {
// launch the application
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// Label for admission type selection
labelAdmission = new Label("Please select your Admission type: ");
// mandatory selection for conference
generalAdmissionButton = new RadioButton("General Admission: $895");
studentAdmissionButton = new RadioButton("Student Admission: $495");
// Create toggle group for either admission group
ToggleGroup optionalEvents = new ToggleGroup();
generalAdmissionButton.setToggleGroup(optionalEvents);
studentAdmissionButton.setToggleGroup(optionalEvents);
// Label for optional conference events
labelOptionalEvents = new Label("Please Select All Optional Events You Will Be Attending: ");
// set values for optional conference events
keynoteDinnerButton = new RadioButton("Keynote Speech Dinner: $30");
eCommerceButton = new RadioButton("Introduction to E-commerce: $295");
webFutureButton = new RadioButton("The Future of the Web: $295");
advancedJavaButton = new RadioButton("Advanced Java Programming: $395");
securityButton = new RadioButton("Network Security: $395");
// Button for calculating total Conference charges
totalCharges = new Button("Calculate Total");
totalCharges.setOnAction(new TotalChargesCalculator());
// create Vbox container and add all labels, buttons
VBox vbox = new VBox(10, labelAdmission, generalAdmissionButton, studentAdmissionButton, labelOptionalEvents,
keynoteDinnerButton, eCommerceButton, webFutureButton, advancedJavaButton, securityButton, totalCharges,
totalChargesLabel);
// format vbox
vbox.setAlignment(Pos.CENTER);
vbox.setPadding(new Insets(20));
// create and set scene
Scene scene = new Scene(vbox);
stage.setTitle("Conference Registration");
stage.setScene(scene);
// show stage
stage.show();
}
class TotalChargesCalculator implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent arg0) {
int result = 0;
try {
// check which radio buttons are selected
if (generalAdmissionButton.isSelected()) {
result = result + 895;
}
if (studentAdmissionButton.isSelected()) {
result = result + 495;
}
if (keynoteDinnerButton.isSelected()) {
result = result + 295;
}
if (eCommerceButton.isSelected()) {
result = result + 295;
}
if (webFutureButton.isSelected()) {
result = result + 295;
}
if (advancedJavaButton.isSelected()) {
result = result + 395;
}
if (securityButton.isSelected()) {
result = result + 395;
}
totalChargesLabel.setText(String.valueOf(result));
} catch (Exception e) {
if (generalAdmissionButton.isSelected() == false || studentAdmissionButton.isSelected() == false) {
totalChargesLabel.setText("Please Select Admission Type.");
}
}
}
}
}
Thanks for your time. I look forward to learning what I am overlooking.
You are not initializing totalChargesLabel.
Initialize it to an empty Label before adding it to the VBox:
totalChargesLabel = new Label();
Related
I have an Alert.INFORMATION in JavaFX with custom "yes" and "no" buttons. In a JavaFX Timeline, when reaching a condition, I would like to display this alert with showAndWait() and then get what the user clicked to close the alert (either "yes" or "no").
Here is what I have so far.
package application;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
String drawingsSelected = "something here";
ButtonType yesButton = new ButtonType("Yes");
ButtonType noButton = new ButtonType("No");
Alert commenceAlert;
#Override
public void start(Stage primaryStage) {
commenceAlert = new Alert(AlertType.INFORMATION);
commenceAlert.setHeaderText("Warning");
commenceAlert.getButtonTypes().setAll(yesButton, noButton);
Button myButton = new Button("click");
myButton.setOnAction(e->quickPick());
VBox root = new VBox(5);
root.getChildren().add(myButton);
Scene myScene = new Scene(root);
primaryStage.setScene(myScene);
primaryStage.show();
}
//randomly select the rest of the card numbers
private void quickPick() {
List<Integer> randomNumbers = new ArrayList<Integer>(Arrays.asList(1,2,3,4));
int spotsLeft = randomNumbers.size();
//add randomNumbers on a time-line
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(100),
e -> {
setCardNumbers(randomNumbers.remove(0));
}
));
timeline.setCycleCount(spotsLeft);
timeline.play();
}
//add i to the card
//at the last addition, call commenceDrawings()
private void setCardNumbers(int i) {
System.out.println("adding "+i);
if(i==4) {
commenceDrawings();
}
}
//called when the conditions are met to start the drawings
public void commenceDrawings() {
commenceAlert.setContentText(drawingsSelected);
Optional<ButtonType> result = commenceAlert.showAndWait();
//player selected "YES"
if(result.get() == yesButton) {
System.out.println("you clicked yes");
}
else //player selected "NO"
{
System.out.println("you clicked no");
}
}
public static void main(String[] args) {
launch(args);
}
}
However, I receive the following error:
Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: showAndWait is not allowed during animation or layout processing
at javafx.scene.control.Dialog.showAndWait(Dialog.java:328)
at application.Main.commenceDrawings(Main.java:56)
at application.Main.setCardNumbers(Main.java:89)
at application.Main.lambda$1(Main.java:77)
at ...
Edit 1: Pausing the Timeline gives the error:
The local variable timeline may not have been initialized
//add randomNumbers on a time-line
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(300),
e -> {
timeline.pause();
setCardNumbers(randomNumbers.remove(0));
timeline.play();
}
));
Edit 2:
I think it works correctly with this edit to setCardNumbers():
//add i to the card
//at the last addition, call commenceDrawings()
private void setCardNumbers(int i) {
System.out.println("adding "+i);
if(i==4) {
Platform.runLater(()-> {
gui.commenceDrawings();
});
}
}
This question already has answers here:
Reading a plain text file in Java
(31 answers)
Closed 5 years ago.
I have a program called "AddUser" that allows the user to type in their username and password, which will add this info to user.txt file. I also have a program called "Login" that takes the information the user inputs, username and password, and verifies the input against the user.txt file.
However, I cannot figure out how to validate the input for the Login program. I have found several other posts here, but not from validating from a text file. Any help or guidance would be GREATLY appreciated.
Program Add User
import javax.swing.JOptionPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.HPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.io.*;
public class AddUser extends Application {
private TextField tfUsername = new TextField();
private TextField tfPassword = new TextField();
private Button btAddUser = new Button("Add User");
private Button btClear = new Button("Clear");
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
// Create UI
GridPane gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setVgap(5);
gridPane.add(new Label("Username:"), 0, 0);
gridPane.add(tfUsername, 1, 0);
gridPane.add(new Label("Password:"), 0, 1);
gridPane.add(tfPassword, 1, 1);
gridPane.add(btAddUser, 1, 3);
gridPane.add(btClear, 1, 3);
// Set properties for UI
gridPane.setAlignment(Pos.CENTER);
tfUsername.setAlignment(Pos.BOTTOM_RIGHT);
tfPassword.setAlignment(Pos.BOTTOM_RIGHT);
GridPane.setHalignment(btAddUser, HPos.LEFT);
GridPane.setHalignment(btClear, HPos.RIGHT);
// Process events
btAddUser.setOnAction(e -> writeNewUser());
btClear.setOnAction(e -> {
tfUsername.clear();
tfPassword.clear();
});
// Create a scene and place it in the stage
Scene scene = new Scene(gridPane, 300, 150);
primaryStage.setTitle("Add User"); // Set title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
public void writeNewUser() {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("users.txt", true))) {
bw.write(tfUsername.getText());
bw.newLine();
bw.write(tfPassword.getText());
bw.newLine();
}
catch (IOException e){
e.printStackTrace();
}
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
*/
public static void main(String[] args) {
launch(args);
}
}
Program Login
import javax.swing.JOptionPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.HPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.io.*;
public class Login extends Application {
private TextField tfUsername = new TextField();
private TextField tfPassword = new TextField();
private Button btAddUser = new Button("Login");
private Button btClear = new Button("Clear");
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
// Create UI
GridPane gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setVgap(5);
gridPane.add(new Label("Username:"), 0, 0);
gridPane.add(tfUsername, 1, 0);
gridPane.add(new Label("Password:"), 0, 1);
gridPane.add(tfPassword, 1, 1);
gridPane.add(btAddUser, 1, 3);
gridPane.add(btClear, 1, 3);
// Set properties for UI
gridPane.setAlignment(Pos.CENTER);
tfUsername.setAlignment(Pos.BOTTOM_RIGHT);
tfPassword.setAlignment(Pos.BOTTOM_RIGHT);
GridPane.setHalignment(btAddUser, HPos.LEFT);
GridPane.setHalignment(btClear, HPos.RIGHT);
// Process events
btClear.setOnAction(e -> {
tfUsername.clear();
tfPassword.clear();
});
// Create a scene and place it in the stage
Scene scene = new Scene(gridPane, 300, 150);
primaryStage.setTitle("Login"); // Set title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
*/
public static void main(String[] args) {
launch(args);
}
}
Consider this Example (Explanation in Comments):
// create boolean variable for final decision
boolean grantAccess = false;
// get the user name and password when user press on login button
// you already know how to use action listener
// (i.e wrap the following code with action listener block of login button)
String userName = tfUsername.getText();
String password = tfPassword.getText();
File f = new File("users.txt");
try {
Scanner read = new Scanner(f);
int noOfLines=0; // count how many lines in the file
while(read.hasNextLine()){
noOfLines++;
}
//loop through every line in the file and check against the user name & password (as I noticed you saved inputs in pairs of lines)
for(int i=0; i<noOfLines; i++){
if(read.nextLine().equals(userName)){ // if the same user name
i++;
if(read.nextLine().equals(password)){ // check password
grantAccess=true; // if also same, change boolean to true
break; // and break the for-loop
}
}
}
if(grantAccess){
// let the user continue
// and do other stuff, for example: move to next window ..etc
}
else{
// return Alert message to notify the deny
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
I am attempting to enable a JavaFX Button depending on the aggregate of a property value in the selected rows in a TableView. The following is an example application that demonstrates the problem:
package test;
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) throws Exception {
launch(args);
}
private static class Row {
private final BooleanProperty myProp;
public Row(final boolean value) {
myProp = new SimpleBooleanProperty(value);
}
public BooleanProperty propProperty() { return myProp; }
}
#Override
public void start(final Stage window) throws Exception {
// Create a VBox to hold the table and button
final VBox root = new VBox();
root.setMinSize(200, 200);
// Create the table, and enable multi-select
final TableView<Row> table = new TableView<>();
final MultipleSelectionModel<Row> selectionModel = table.getSelectionModel();
selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
root.getChildren().add(table);
// Create a column based on the value of Row.propProperty()
final TableColumn<Row, Boolean> column = new TableColumn<>("Value");
column.setCellValueFactory(p -> p.getValue().propProperty());
table.getColumns().add(column);
// Add a button below the table
final Button button = new Button("Button");
root.getChildren().add(button);
// Populate the table with true/false values
final ObservableList<Row> rows = table.getItems();
rows.addAll(new Row(false), new Row(false), new Row(false));
// Start a thread to randomly modify the row values
final Random rng = new Random();
final Thread thread = new Thread(() -> {
// Flip the value in a randomly selected row every 10 seconds
try {
do {
final int i = rng.nextInt(rows.size());
System.out.println("Flipping row " + i);
Thread.sleep(10000);
final BooleanProperty prop = rows.get(i).propProperty();
prop.set(!prop.get());
} while (true);
} catch (final InterruptedException e) {
System.out.println("Exiting Thread");
}
}, "Row Flipper Thread");
thread.setDaemon(true);
thread.start();
// Bind the button's disable property such that the button
// is only enabled if one of the selected rows is true
final ObservableList<Row> selectedRows = selectionModel.getSelectedItems();
button.disableProperty().bind(Bindings.createBooleanBinding(() -> {
for (int i = 0; i < selectedRows.size(); ++i) {
if (selectedRows.get(i).propProperty().get()) {
return false;
}
}
return true;
}, selectedRows));
// Show the JavaFX window
final Scene scene = new Scene(root);
window.setScene(scene);
window.show();
}
}
To test, start the above application, and select the row indicated by the text "Flipping row N", where N is in [0, 2]. When the value of the selected row changes to true...
Observed Behavior button remains disabled.
Desired Behavior button becomes enabled.
Does anyone know how to create a BooleanBinding that exhibits the desired behavior?
Your binding needs to be invalidated if any of the propPropertys of the selected rows change. Currently the binding is only observing the selected items list, which will fire events when the list contents change (i.e. items become selected or unselected) but not when properties belonging to items in that list change value.
To do this, create a list with an extractor:
final ObservableList<Row> selectedRows =
FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()});
This list will fire events when items are added or removed, or when the propProperty() of any item in the list changes. (If you need to observe multiple values, you can do so by including them in the array of Observables.)
Of course, you still need this list to contain the selected items in the table. You can ensure this by binding the content of the list to the selectedItems of the selection model:
Bindings.bindContent(selectedRows, selectionModel.getSelectedItems());
Here is a version of your MCVE using this:
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) throws Exception {
launch(args);
}
private static class Row {
private final BooleanProperty myProp;
public Row(final boolean value) {
myProp = new SimpleBooleanProperty(value);
}
public BooleanProperty propProperty() { return myProp; }
}
#Override
public void start(final Stage window) throws Exception {
// Create a VBox to hold the table and button
final VBox root = new VBox();
root.setMinSize(200, 200);
// Create the table, and enable multi-select
final TableView<Row> table = new TableView<>();
final MultipleSelectionModel<Row> selectionModel = table.getSelectionModel();
selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
root.getChildren().add(table);
// Create a column based on the value of Row.propProperty()
final TableColumn<Row, Boolean> column = new TableColumn<>("Value");
column.setCellValueFactory(p -> p.getValue().propProperty());
table.getColumns().add(column);
// Add a button below the table
final Button button = new Button("Button");
root.getChildren().add(button);
// Populate the table with true/false values
final ObservableList<Row> rows = table.getItems();
rows.addAll(new Row(false), new Row(false), new Row(false));
// Start a thread to randomly modify the row values
final Random rng = new Random();
final Thread thread = new Thread(() -> {
// Flip the value in a randomly selected row every 10 seconds
try {
do {
final int i = rng.nextInt(rows.size());
System.out.println("Flipping row " + i);
Thread.sleep(10000);
final BooleanProperty prop = rows.get(i).propProperty();
Platform.runLater(() -> prop.set(!prop.get()));
} while (true);
} catch (final InterruptedException e) {
System.out.println("Exiting Thread");
}
}, "Row Flipper Thread");
thread.setDaemon(true);
thread.start();
// Bind the button's disable property such that the button
// is only enabled if one of the selected rows is true
final ObservableList<Row> selectedRows =
FXCollections.observableArrayList(r -> new Observable[]{r.propProperty()});
Bindings.bindContent(selectedRows, selectionModel.getSelectedItems());
button.disableProperty().bind(Bindings.createBooleanBinding(() -> {
for (int i = 0; i < selectedRows.size(); ++i) {
if (selectedRows.get(i).propProperty().get()) {
return false;
}
}
return true;
}, selectedRows));
// Show the JavaFX window
final Scene scene = new Scene(root);
window.setScene(scene);
window.show();
}
}
I am still pretty new to java and i am still learning. I have never used images before so it is possible if I can have help add a image. I am not that sure what needs to be done in order to add one. Thank you
enter image description here
here is the code:
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
class User {
private StringProperty order = new SimpleStringProperty();
public String getOrder() {
return order.get();
}
public void setOrder(String order) {
this.order.set(order);
}
public StringProperty orderProperty() {
return order;
}
}
public class pizza extends Application {
private User user = new User();
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Pizza System");
Button btn = new Button();
btn.setText("place order");
BorderPane pane = new BorderPane();
pane.setBottom(btn);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
btn.setText("Order has been placed. Please wait at least 30 minutes.");
}
});
RadioButton tomatoButton = new RadioButton("Tomato");
RadioButton pepperButton = new RadioButton("Pepper");
RadioButton mushroomButton = new RadioButton("Mushrooms");
ChoiceBox<String> pizzaType = new ChoiceBox<String>();
pizzaType.getItems().addAll("", "Small", "Medium", "Large");
pizzaType.getSelectionModel().selectFirst();
HBox topHBox = new HBox(15.0, tomatoButton, pepperButton, mushroomButton, pizzaType);
// create custom Binding that binds selection of radio buttons and choice box
StringBinding orderBinding = createOrderBinding(tomatoButton.selectedProperty(), pepperButton.selectedProperty(), mushroomButton.selectedProperty(), pizzaType.getSelectionModel().selectedItemProperty());
// bind orderBinding to orderProperty of User
user.orderProperty().bind(orderBinding);
TextArea orderArea = new TextArea();
// bind orderProperty of User to textProperty of TextArea
orderArea.textProperty().bindBidirectional(user.orderProperty());
BorderPane root = new BorderPane();
root.setTop(topHBox);
root.setCenter(orderArea);
root.setBottom(btn);
Scene scene = new Scene(root, 400, 300);
stage.setScene(scene);
stage.show();
}
public StringBinding createOrderBinding(BooleanProperty tomato, BooleanProperty pepper, BooleanProperty mushroom, ReadOnlyObjectProperty<String> selectedPizzaType) {
StringBinding binding = new StringBinding() {
{
// bind 4 provided properties.
super.bind(tomato, pepper, mushroom, selectedPizzaType);
}
#Override
protected String computeValue() {
StringBuilder sb = new StringBuilder("Pizza content:\n");
if (tomato.get())
sb.append("\tTomato\n");
if (pepper.get())
sb.append("\tPepper\n");
if (mushroom.get())
sb.append("\tMushroom\n");
sb.append("Pizza type:\n").append("\t" + selectedPizzaType.get());
return sb.toString();
}
};
return binding;
}
public static void main(String[] args) {
Application.launch(args);
}
}
JavaFX uses an Image to load the image file and it has a node called ImageView to place that image on the screen graph.
Considering that the image is present at the same location as your class file, you can use this:
// Load Image
Image image = new Image(getClass().getResource("image.jpg").toExternalForm());
// Set the Image on the ImageView
ImageView imageView = new ImageView(image);
// specify a size
imageView.setFitWidth(200);
imageView.setFitHeight(200);
// Place ImageView in a container
root.setRight(imageView);
I have a ListView with a TextField above it. If a user enters in a search query into the textfield, the listview will update and filter itself to show relevant results.
The ListView shows items from a FilteredList, which is filled with Employee objects. Each Employee has a first and last name.
package application.ctrl;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import application.Main;
import application.objects.Employee;
import application.objects.EmployeeDatabase;
public class EmployeePickerWidget extends VBox implements Initializable {
#FXML
private TextField textField;
#FXML
private Button addNewEmployee;
#FXML
private ListView<Employee> employeeList;
private FilteredList<Employee> filteredList;
private ContextMenu cm;
private CustomMenuItem item;
private ClickedEmployeeInterface parent;
public EmployeePickerWidget(ClickedEmployeeInterface parent) {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource(
Main.EMPLOYEE_PICKER));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.parent = parent;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
setupEmployeeListView();
setupTextField();
}
private void setupEmployeeListView() {
filteredList = new FilteredList<Employee>(EmployeeDatabase.getInstance()
.getObservableList());
employeeList = new ListView<Employee>();
employeeList.setItems(filteredList);
employeeList.setOnMouseClicked(arg0 -> {
if (employeeList.getSelectionModel().getSelectedItem() != null) {
cm.hide();
parent.handleClickedEmployee();
}
});
}
private void setupTextField() {
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
filteredList.setPredicate(employee -> {
return filterHelper(employee, newValue);
});
});
textField.setText(" ");
textField.setText("");
textField.setOnMouseClicked(event -> cm
.show(textField, Side.BOTTOM, 0, 0));
cm = new ContextMenu();
item = new CustomMenuItem();
VBox container = new VBox();
container.setAlignment(Pos.CENTER_RIGHT);
container.getChildren().add(employeeList);
Button defineEmployeeBtn = new Button("Define New Employee");
defineEmployeeBtn.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
Main.DEFINE_NEW_EMPLOYEE));
Parent root = null;
try {
root = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
});
container.getChildren().add(defineEmployeeBtn);
item.setContent(container);
cm.getItems().add(item);
}
private boolean filterHelper(Employee employee, String query) {
String first = employee.getFirst().toLowerCase(), last = employee
.getLast().toLowerCase();
String[] querySplit = query.replace(",", "\\s").split("\\s+");
int length = querySplit.length;
for (int i = 0; i < length; i++)
querySplit[i] = querySplit[i].toLowerCase();
if (length == 1) {
if (first.contains(querySplit[0]) || last.contains(querySplit[0]))
return true;
else
return false;
} else if (length == 2) {
if (first.contains(querySplit[0]) || last.contains(querySplit[0]))
if (first.contains(querySplit[1]) || last.contains(querySplit[1]))
return true;
return false;
} else if (length == 3) {
return false;
}
return false;
}
public Employee getEmployee() {
return employeeList.getSelectionModel().getSelectedItem();
}
#FXML
public void addNewEmployee() {
}
}
interface ClickedEmployeeInterface {
void handleClickedEmployee();
}
If there were 3 employees named "Donald Trump", "Donald Smith", and "Donald Jackson" in the database, then the following needs to happen:
Typing up to the word "Donald" will show all 3 results.
Typing a space after Donald (resulting in "Donald ") will still show 3 results.
Typing a T after the previous query (resulting in "Donald T") should only show 1 result.
The problem is, after I enter in a space, the ListView breaks, and all of my Employees disappear from the ListView. When I click outside of the textfield and click back in again, it triggers this:
textField.setOnMouseClicked(event -> cm
.show(textField, Side.BOTTOM, 0, 0));
And my ListView suddenly works again, showing that one Employee.
How do I make the ListView filter properly without having to click out and back in?
I do not have the FXML file, so I wasn't able to replicate your problem. There are multiple problems with your code and this is the not the optimum solution, still, I have edited your answer to give you hints and help you understand the areas where you might have committed logical errors
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class DemoList extends Application {
#Override
public void start(Stage stage) throws Exception {
GridPane gridPane = new GridPane();
Label label = new Label("Name");
final TextField textField = new TextField();
textField.setFocusTraversable(false);
textField.setPromptText("Please Type Here");
final ContextMenu cm = new ContextMenu();
final ObservableList<String> employeeList = FXCollections
.observableArrayList();
employeeList.addAll("Donald Duck", "Donald Mouse", "Donald Goofy");
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> arg0,
String arg1, String arg2) {
// To clear the Context Menu so that same items are not added
// multiple times
cm.getItems().clear();
for (String employee : employeeList) {
if (filterHelper(employee, arg2)) {
cm.getItems().add(new MenuItem(employee));
}
}
}
});
textField.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
// To clear the Context Menu so that same items are not added
// multiple times
cm.getItems().clear();
//Adding the data for initial click
for (String employee : employeeList) {
if (filterHelper(employee, textField.getText())) {
cm.getItems().add(new MenuItem(employee));
}
}
cm.show(textField, Side.BOTTOM, 0, 0);
}
});
gridPane.add(label, 0, 0);
gridPane.add(textField, 0, 1);
Scene scene = new Scene(gridPane, 300, 300);
stage.setScene(scene);
stage.show();
}
private boolean filterHelper(String employee, String query) {
//Splitting Employee name to fetch first and last name
String first = employee.split(" ")[0].toLowerCase(), last = employee
.split(" ")[1].toLowerCase();
String[] querySplit = query.replace(",", "\\s").split("\\s+");
int length = querySplit.length;
for (int i = 0; i < length; i++)
querySplit[i] = querySplit[i].toLowerCase();
/**
* Avoid adding unnecessary return statement
* I have removed all the 'return false' statements
* The last return will take care of all the 'return false'
*/
//only single word
if (length == 1) {
if (first.startsWith(querySplit[0])
|| last.startsWith(querySplit[0]))
return true;
}
//two words, considering first word is first name
//and second word is last name
else if (length == 2) {
if (first.startsWith(querySplit[0])
&& last.startsWith(querySplit[1]))
return true;
}
return false;
}
public static void main(String[] args) {
launch(args);
}
}