JavaFx: show DatePicker - java

I have a ComboBox<MyItem> and I want to show a DatePicker when I select a special item from the ComboBox. I created a class that extends ComboBox, and I have a DatePicker in that class. I have added a listener to its selectedItemProperty:
public class CustomComboBox extends ComboBox<MyItem>{
private DatePicker datePicker;
public CustomComboBox(){
getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (MyItem.DATE.equals(newValue)) {
initDatePicker();
datePicker.show();
datePicker.requestFocus();
}
});
}
private void initDatePicker() {
if (datePicker == null) {
datePicker = new DatePicker();
datePicker.setFocusTraversable(false);
}
}
}
So if I select the DATE item the DatePicker should pop up and If I select a date I want to add as the value of the ComboBox
First of all why the datePicker not pops up? The second question is this posible to add the selected date to comboBox as value.

I assume you need something like this:
I did it by using a popup class from ControlsFX library.
Play with this demo app to understand the main idea.
import org.controlsfx.control.PopOver;
// here all other needed dependencies
public class Main extends Application {
private static final String DATE_TYPE = "DATE";
private class ComboBoxNode {
private Object value;
private String type;
private ComboBoxNode(final Object value, final String type) {
this.value = value;
this.type = type;
}
#Override
public String toString() {
return Objects.toString(value);
}
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final ObservableList<ComboBoxNode> items =
FXCollections.observableArrayList(
new ComboBoxNode(LocalDate.now(), DATE_TYPE),
new ComboBoxNode("11:35AM", "TIME"));
final PopOver datePopOver = new PopOver();
datePopOver.setTitle("Enter new date");
datePopOver.setCornerRadius(10);
datePopOver.setHeaderAlwaysVisible(true);
datePopOver.setCloseButtonEnabled(true);
datePopOver.setAutoHide(true);
final ComboBox<ComboBoxNode> customComboBox = new ComboBox<>(items);
customComboBox.getSelectionModel().selectedItemProperty().addListener((o, old, newNode) -> {
if (newNode != null) {
if (newNode.type.equals(DATE_TYPE)) {
final DatePicker datePicker = new DatePicker((LocalDate) newNode.value);
datePicker.valueProperty().addListener((obs, oldDate, newDate) -> {
items.set(customComboBox.getSelectionModel().getSelectedInde‌​x(), new ComboBoxNode(newDate, DATE_TYPE));
datePopOver.hide();
});
final StackPane stackPane = new StackPane(datePicker);
stackPane.setPadding(new Insets(10, 10, 10, 10));
datePopOver.setContentNode(stackPane);
datePopOver.show(customComboBox);
} else {
datePopOver.hide();
}
}
});
final FlowPane pane = new FlowPane(customComboBox);
pane.setPadding(new Insets(10, 10, 10, 10));
pane.setPrefWidth(400);
pane.setPrefHeight(300);
// Show Scene
final Scene scene = new Scene(pane);
primaryStage.setTitle("Popup calendar");
primaryStage.setScene(scene);
primaryStage.show();
}
}

Related

How do I call back to the main node so I can switch to a different scene in javafx

so I am trying to switch to a different scene in javafx. I am working on a project where the user will select on a food category via a combobox, which will switch to a new scene that has all the recipes under that specific category.
The problem is I am able to load my MainMenuScene, but I am not sure how to switch to a different scene, which I think is because of how I have structured my code.
My code is structured where I can GUIAPP (which starts the entire GUI)
private static View myView;
private static Recipe myRecipe;
public GUIApp() {
myView = new View();
myRecipe = new Recipe();
}
public static View getView() {
return myView;
}
public static Recipe getRecipe() {
return myRecipe;
}
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println("Starting app...");
myView.setStage(primaryStage);
}
public static void main(String [] args) {
myRecipe = new Recipe();
launch(args);
}
}
Then View, which sets the primary Stage (which is MainMenuScene)
private Stage myStage;
private Scene myMainMenuScene;
private Scene myMealScene;
public View() {
myMainMenuScene = new MainMenuScene(new BorderPane());
}
public Stage getStage() {
return myStage;
}
public Scene getMealScene() {
return myMealScene;
}
public void setStage(Stage primaryStage) {
myStage = primaryStage;
myStage.setScene(myMainMenuScene);
myStage.setTitle("Recipe Database");
myStage.setWidth(600);
myStage.setHeight(400);
Scene scene = myMainMenuScene;
myStage.setScene(scene);
myStage.show();
}
}
Here is MainMenuScene, where it calls the recipe code I had previously made
private BorderPane myRoot;
private String f, t;
Label label = new Label();
public MainMenuScene(Parent root) {
super(root);
myRoot = (BorderPane) root;
BorderPane titleBox = new BorderPane();
titleBox.setStyle("-fx-font: 24 arial;");
myRoot.setTop(titleBox);
Text title = new Text("Recipe Database Tool");
title.setFont(new Font(50));
titleBox.setCenter(title);
//VBox and HBox creation
VBox leftBox = new VBox();
VBox rightBox = new VBox();
VBox centerBox = new VBox();
//Button search = new Button("Recipe Categories");
Button create = new Button("Create/Upload a New Recipe");
//Creates a combobox that gets the category name from Recipe
ComboBox<String> search = new ComboBox<String>();
search.getItems().addAll(GUIApp.getRecipe().getCategory());
search.setOnAction((e) -> {
search.getSelectionModel().selectFirst();
});
/*
****//Create a button that switches to MealScene when a category is selected
Button select = new Button("Select");
select.setOnAction((e) -> {
View.getStage().setScene(View.getMealScene());****
});
*/
//myRoot for each Box
myRoot.setLeft(leftBox);
myRoot.setRight(rightBox);
myRoot.setCenter(centerBox);
//adding the search categories button to the left box
leftBox.setAlignment(Pos.CENTER);
leftBox.setSpacing(30);
leftBox.getChildren().add(search);
//adding the create a new recipe to the right box
rightBox.setAlignment(Pos.CENTER);
rightBox.setSpacing(30);
rightBox.getChildren().add(create);
}
}
I tried calling the button by using View, but I am getting an error that I am not sure how to fix.
Here's the error: It's asking for View.getStage() to make getStage a static method in View, which I don't want to do and View.getMealScene to also become static
Please let me know! I would love to learn how to prevent this in the future

how to switching scenes in javafx (NO FXML)

I ask for your understanding, I am a beginner;)
I'm trying to build a simple application using JavaFX. The problem is that when I open the window the first time it goes well, but if I want to change the scene it throws an error...
Exception in thread "JavaFX Application Thread"
java.lang.IllegalArgumentException:
AnchorPane#1809546[styleClass=root]is already set as root of another
scene#
Main class
public class Main extends Application{
//private Stage primaryStage;
#Override
public void start(Stage primaryStage) {
Login login = new Login();
Scene scene = login.okno();
primaryStage.setTitle("Komunikator sieciowy JAVA");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
//public Stage getPrimaryStage() {
// return this.primaryStage;
//}
public static void main(String[] args) {
launch(args);
}
}
Login
public class Login {
private GridPane grid;
private Scene scene;
private Text title;
private Label nick;
private Button wejdzBtn;
private TextField userName;
//private Alert oknoDlg;
public Login() {
grid = new GridPane();
grid.setAlignment (Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25,25,25,25));
scene = new Scene (grid, 300, 150);
utworzBtn();
utworzLogin();
utworzTekst();
utworzNick();
//oknoDialogowe();
}
//private void oknoDialogowe() {
//Alert oknoDlg = new Alert(Alert.AlertType.CONFIRMATION);
//oknoDlg.setTitle("Informacja");
//oknoDlg.setContentText("test");
// oknoDlg.setHeaderText(null);
//oknoDlg.showAndWait();
//}
private void utworzBtn() {
wejdzBtn = new Button("Zaloguj si\u0119");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment (Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(wejdzBtn);
grid.add(hbBtn, 1, 2);
//wejdzBtn.setDisable(true);
wejdzBtn.setOnAction(e -> {
Messages mess = new Messages();
grid.getScene().setRoot(mess.messa());;
});
}
private void utworzLogin() {
nick = new Label("Nick:");
grid.add(nick, 0, 1);
}
private void utworzNick() {
userName = new TextField();
grid.add(userName,1,1);
// informacja w polu tekstowym
userName.setPromptText("Max 15 znak\u00f3w");
userName.setFocusTraversable(false);
//maksymalna ilość znaków
final int maxLength = 15;
userName.setOnKeyTyped(t -> {
if (userName.getText().length() > maxLength)
{
int pos = userName.getCaretPosition();
userName.setText(userName.getText(0, maxLength));
userName.positionCaret(pos);
}
});
}
private void utworzTekst() {
title = new Text ("Dzień dobry!");
title.setFont(Font.font("Calibri", FontWeight.NORMAL, 20));
grid.add(title, 0, 0, 2, 1);
}
public Scene okno() {
return scene;
}
}
and and a little another class that I'm trying to change with button from login.java
public class Messages {
private AnchorPane anchor;
private Scene scena;
//private Label nick;
private Button sendBtn;
private TextField poleDoWpisywania;
private TextArea poleDoWyswietlania, pobierzNick;
public Messages() {
anchor = new AnchorPane();
scena = new Scene(anchor, 700, 600);
pobierzNick();
poleDoWpisywania();
poleDoWyswietlania();
utworzPrzycisk();
}
private void utworzPrzycisk() {
sendBtn = new Button("Wy\u015Blij");
sendBtn.setDisable(true);
}
private void pobierzNick(){
pobierzNick = new TextArea();
pobierzNick.setEditable(false);
pobierzNick.setWrapText(true);
}
private void poleDoWpisywania() {
poleDoWpisywania = new TextField();
}
private void poleDoWyswietlania() {
poleDoWyswietlania = new TextArea();
poleDoWyswietlania.setEditable(false);
poleDoWyswietlania.setWrapText(true);
}
public Pane messa() {
return anchor;
}
}
could I ask you to show the right way to fix the bug?
JavaFX defines a scene graph which is a tree data structure that has a single root node. For your application (i.e. the code you posted), the root node is the primaryStage (this is the parameter in method start() in class Main). The primaryStage can have several Scenes. Each Scene must have its own root node.
The error message you are getting means that a Scene's root cannot also be the root of another Scene. In other words anchor is the root for scena in class Messages which means it can't be set as the root for scene in class Login.
Apart from that, if you want to change Scene's you need to call method setScene() of class Stage. Here is your Login class and Messages class with changes that solve the run-time error you are getting and perform the scene change when the user clicks on wejdzBtn button.
Login.java
(I only changed the lambda expression in method utworzBtn().)
public class Login {
private GridPane grid;
private Scene scene;
private Text title;
private Label nick;
private Button wejdzBtn;
private TextField userName;
public Login() {
grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25,25,25,25));
scene = new Scene(grid, 300, 150);
utworzBtn();
utworzLogin();
utworzTekst();
utworzNick();
}
private void utworzBtn() {
wejdzBtn = new Button("Zaloguj si\u0119");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment (Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(wejdzBtn);
grid.add(hbBtn, 1, 2);
wejdzBtn.setOnAction(e -> {
Messages mess = new Messages();
Window w = scene.getWindow();
if (w instanceof Stage) {
Stage s = (Stage) w;
s.setScene(mess.getScena());
}
});
}
private void utworzLogin() {
nick = new Label("Nick:");
grid.add(nick, 0, 1);
}
private void utworzNick() {
userName = new TextField();
grid.add(userName,1,1);
userName.setPromptText("Max 15 znak\u00f3w");
userName.setFocusTraversable(false);
final int maxLength = 15;
userName.setOnKeyTyped(t -> {
if (userName.getText().length() > maxLength)
{
int pos = userName.getCaretPosition();
userName.setText(userName.getText(0, maxLength));
userName.positionCaret(pos);
}
});
}
private void utworzTekst() {
title = new Text ("Dzień dobry!");
title.setFont(Font.font("Calibri", FontWeight.NORMAL, 20));
grid.add(title, 0, 0, 2, 1);
}
public Scene okno() {
return scene;
}
}
Messages.java
(I added method getScena().)
public class Messages {
private AnchorPane anchor;
private Scene scena;
private Button sendBtn;
private TextField poleDoWpisywania;
private TextArea poleDoWyswietlania, pobierzNick;
public Messages() {
anchor = new AnchorPane();
scena = new Scene(anchor, 700, 600);
pobierzNick();
poleDoWpisywania();
poleDoWyswietlania();
utworzPrzycisk();
}
private void utworzPrzycisk() {
sendBtn = new Button("Wy\u015Blij");
sendBtn.setDisable(true);
}
private void pobierzNick() {
pobierzNick = new TextArea();
pobierzNick.setEditable(false);
pobierzNick.setWrapText(true);
}
private void poleDoWpisywania() {
poleDoWpisywania = new TextField();
}
private void poleDoWyswietlania() {
poleDoWyswietlania = new TextArea();
poleDoWyswietlania.setEditable(false);
poleDoWyswietlania.setWrapText(true);
}
public Scene getScena() {
return scena;
}
public Pane messa() {
return anchor;
}
}
Thanks a lot Abra, I've been thinking about it for the last 6 hours and haven't noticed this problem. I have also removed
public Pane messa ();
return anchor;
I do not need it ;)

Create a listener logic for a custom combobox

I am trying to create a listener logic for a custom combo box that I have created that contains items with check boxes.
I was not able to proceed as I am not getting an idea on how to do it.
MainApplication.java
public class MainApplication extends Application{
#Override
public void start(Stage stage) {
Scene scene = new Scene(new VBox(), 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
#SuppressWarnings("unchecked")
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994"))
);
cb.setCellFactory( c -> {
ListCell<ComboBoxItemWrap<Person>> cell = new ListCell<ComboBoxItemWrap<Person>>(){
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
final CheckBox cb = new CheckBox(item.toString());
cb.selectedProperty().bind(item.checkProperty());
setGraphic(cb);
}
}
};
cell.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
cell.getItem().checkProperty().set(!cell.getItem().checkProperty().get());
StringBuilder sb = new StringBuilder();
cb.getItems().filtered( f-> f!=null).filtered( f-> f.getCheck()).forEach( p -> {
sb.append("; "+p.getItem());
});
final String string = sb.toString();
cb.setPromptText(string.substring(Integer.min(2, string.length())));
});
return cell;
});
cb.setItems(options);
VBox root = (VBox) scene.getRoot();
Button bt = new Button("test");
bt.setOnAction(event -> {
cb.getItems().filtered( f -> f.getCheck()).forEach( item -> System.out.println(item.getItem()));
});
root.getChildren().addAll(cb, bt);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ComboBoxItemWrap.java
public class ComboBoxItemWrap<T> {
private BooleanProperty check = new SimpleBooleanProperty(false);
private ObjectProperty<T> item = new SimpleObjectProperty<>();
ComboBoxItemWrap() {
}
ComboBoxItemWrap(T item) {
this.item.set(item);
}
ComboBoxItemWrap(T item, Boolean check) {
this.item.set(item);
this.check.set(check);
}
public BooleanProperty checkProperty() {
return check;
}
public Boolean getCheck() {
return check.getValue();
}
public void setCheck(Boolean value) {
check.set(value);
}
public ObjectProperty<T> itemProperty() {
return item;
}
public T getItem() {
return item.getValue();
}
public void setItem(T value) {
item.setValue(value);
}
#Override
public String toString() {
return item.getValue().toString();
}
}
Person.java
public class Person {
private StringProperty name = new SimpleStringProperty();
private StringProperty birthday = new SimpleStringProperty();
public Person() {
}
public Person(String name, String birthday) {
setNameValue(name);
setBirthdayValue(birthday);
}
public StringProperty getNameProperty() {
return name;
}
public String getNameValue() {
return name.getValue();
}
public void setNameValue(String value) {
name.setValue(value);
}
public StringProperty getBirthdayProperty() {
return birthday;
}
public String getBirthdayValue() {
return birthday.getValue();
}
public void setBirthdayValue(String value) {
birthday.setValue(value);
}
#Override
public String toString() {
return getNameValue()+" ("+getBirthdayValue()+")";
}
}
In the output application, a list of items with check boxes will get populated. On selection of any number of entries in the list, the entry name gets populated on the combo box itself separated by a ';'. Now I want my back end code to listen and identify the entries that have been selected in order to perform further operations.
You may not need to reinvent the wheel. Consider using ControlsFX CheckComboBox.
That being said there are several problems in the code:
You never update the property on a selection of the CheckBox. This can be easily fixed by using bidirectional bindings.
Since the ComboBox popup is closed, the CheckBox is no longer armed at the time the MOUSE_RELEASED event is triggered. this is a prerequesite for the selected state of the CheckBox changing though. Modifying the skin allows you to change this behaviour.
You use ObservableList.filtered to create FilteredLists that you throw away immediately afterwards. You also create a filtered list of a filtered list in the MOUSE_RELEASED event filter. This is not wrong per se, but you're creating an expensive object there without the need to do so: simply get a stream there. This is a much more lightweight way to filter a list, if the result is only needed once. Use filtered/FilteredList only if you need an ObservableList that contains elements from another ObservableList and that is automatically updated.
Also note that there is a way to make an ObservableList trigger update changes on a change of a property: Use the observableArrayList method taking an extractor as parameter.
This is how you could rewrite your code to make it work:
VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(item -> new Observable[] {item.checkProperty()});
options.addAll(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994")));
cb.setCellFactory(c -> new ListCell<ComboBoxItemWrap<Person>>() {
private final CheckBox cb = new CheckBox();
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
ComboBoxItemWrap<Person> oldItem = getItem();
if (oldItem != null) {
// remove old binding
cb.selectedProperty().unbindBidirectional(oldItem.checkProperty());
}
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
cb.selectedProperty().bindBidirectional(item.checkProperty());
cb.setText(item.toString());
setGraphic(cb);
}
}
});
// make sure popup remains open
ComboBoxListViewSkin<ComboBoxItemWrap<Person>> skin = new ComboBoxListViewSkin<>(cb);
skin.setHideOnClick(false);
cb.setSkin(skin);
cb.setItems(options);
cb.promptTextProperty().bind(Bindings.createStringBinding(() ->
options.stream().filter(ComboBoxItemWrap::getCheck).map(Object::toString).collect(Collectors.joining("; ")), options));
Note that if you want the popup to be closed after (de)selecting a checkbox, you could simply add a event filter for MOUSE_RELEASED for the checkbox that calls cb.arm() instead of modifying the skin.

JavaFX TableView Issues Adding Rows + Organizing Multi Controller and FXML App

I'm new to JavaFX and I've been at this code for about 8 hours now and I've become a bit delusional with the code. My two main problems are:
Can't add new items to the TableView using my popUp box display().
Feels messy and unorganized. Any tips for better communication between FXML and Controllers? (Again I'm new so it could be that I've stared too long at it)
My main class
public class Main extends Application {
public static Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage window) throws Exception {
try {
primaryStage = new Stage();
window = primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("Fissto.fxml"));
Scene scene = new Scene(root);
window.setTitle("Fissto - the File Storage App!");
window.setScene(scene);
window.show();
}catch(Exception e){
e.printStackTrace();
}
// C.setLibraryStage();
}
}
My main Controller class (I have two sub ones that connect in the Fissto.fxml)
public class Controller implements Initializable{
Main main;
#FXML LibraryController libraryController = new LibraryController();
#FXML MergePageController mergePageController = new MergePageController();
private AddImageController addImageController = new AddImageController();
#FXML public void initialize(URL location, ResourceBundle resources){
System.out.println("View is now loaded!");
main = new Main();
libraryController.init(this);
mergePageController.init(this);
addImageController.init(this);
}
//Interface Initialization
public void setMergeStage() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Controllers/MergePage.fxml"));
Scene scene = new Scene(root);
main.primaryStage.setScene(scene);
}
public void setLibraryStage() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Controllers/LibraryPage.fxml"));
Scene scene = new Scene(root);
main.primaryStage.setScene(scene);
}
//Closing a window
public void closeWindow(){
main.primaryStage.close();
}
}
And finally the controller for the page that holds the TableView
public class LibraryController {
private Controller main;
//Library TableView Controllers
#FXML public TableView<Image> library;
#FXML private TableColumn<Image, String> NameColumn = new TableColumn<>();
#FXML private TableColumn<Image, ArrayList<String>> TagsColumn = new TableColumn<>();
#FXML private TableColumn<Image, String> CommentsColumn = new TableColumn<>();
#FXML private TableColumn<Image, String> FileLocationColumn = new TableColumn<>();
#FXML private TableColumn<Image, Integer> PointsColumn = new TableColumn<>();
public void init(Controller main){
System.out.println("LibraryPage Loading");
this.main = main;
addDataToColumns();
library = new TableView<>();
library.getItems().setAll(getImages());
System.out.println("LibraryPage Loaded");
}
//Initializes the column titles
private void addDataToColumns(){
NameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
TagsColumn.setCellValueFactory(new PropertyValueFactory<>("tags")); //TODO Convert to String format
CommentsColumn.setCellValueFactory(new PropertyValueFactory<>("comments"));
FileLocationColumn.setCellValueFactory(new PropertyValueFactory<>("filelocation"));
PointsColumn.setCellValueFactory(new PropertyValueFactory<>("points"));
}
//Gets all of the images
private ObservableList<Image> getImages() {
//TODO: Add where to actually get the data from
ObservableList<Image> images = FXCollections.observableArrayList();
String s = "Dog, Cat, Jumping Jack,";
ArrayList<String> list = Image.getTagOrganizer(',', s);
images.add(new Image("Test", list, "Comment", "No File Location, yet!", 10));
String k = "Calculus, Complex Numbers, Hard dude,";
ArrayList<String> list2 = Image.getTagOrganizer(',', k);
images.add(new Image("Number2", list2, "I love MathClub", "No File Location, yet!", -10));
return images;
}
This last class is the popup menu that takes in input to put in the GridPane
public class AddImageController {
private Controller main;
public void init(Controller main){
System.out.println("ImagePage Loaded");
this.main = main;
}
//Submitting an image to the library from the AddImagePage
public TextField nameInput;
public TextField tagsInput;
public TextField commentInput;
public TextField pointsInput;
public Label errorMessage;
/** TODO: Make it so that it writes to file then theoretically, the main controller should read from file every so often
* Main functionality for adding the information from the form to the database */
public void submitImage(){
if(!(nameInput.getText().trim().isEmpty()) && !(tagsInput.getText().trim().isEmpty()) && !(pointsInput.getText().trim().isEmpty())) {
if (isInt(pointsInput)) {
// System.out.print("Sent to database, *whoosh!*");
LibraryController c = new LibraryController();
ArrayList<String> s = Image.getTagOrganizer(',', tagsInput.getText());
Image image = new Image(nameInput.getText(), s, commentInput.getText(),"Location Needed", Integer.parseInt(pointsInput.getText()));
c.library.getItems().add(image);
clearInputs();
}
}else {
errorMessage.setText("Fill every field");
}
}
//Clears the input fields in the AddImagePage
public void clearInputs(){
nameInput.clear();
tagsInput.clear();
commentInput.clear();
pointsInput.clear();
errorMessage.setText("");
}
//Submission format verifiers
private boolean isInt(TextField input){
try{
int i = Integer.parseInt(input.getText());
errorMessage.setText("");
return true;
}catch (NumberFormatException e){
System.out.println("Oh no: " + input.getText() + " is not an integer");
errorMessage.setText("Points must be a number");
return false;
}
}
//Image Selection Handler
public void imageSelectionHandler(){
}
}
I understand it may be hard to read, so any feedback on how to make it easier to read in the future is much appreciated.

menu items and modal windows are multiplying every time I open them

I've created a vaadin table. When I do right-click it shows context menu with +New... text and when I click on it - it shows modal window with two tables. Every table has the same fuctionality.
The problem is that every time I open and close modal window it adds duplicates for context menu items on modal tables(on the main page it works correct). Moreover - it adds several modal windows when I click on modal table context menu (for example if I open window 5 times - it add 5 context menu items and 5 modal windows for clicked modal context menus)
The only way to return to one item - restart whole application.
What is the problem?
Every my table looks like this
#Component("taskTable")
#Scope("prototype")
public class TaskTable extends AbstractObjectTable {
#Autowired
private TaskService taskService;
#Autowired
private NewTaskWindow taskWindow;
#Autowired
private ShowTaskDetailsWindow detailsWindow;
private Action[] action = new Action[] { new Action("+New...") };
#Override
public Table createTable() {
caption = "Tasks";
headers = new String[] { "Description", "Project", "Status", "Weight", "Developer", "ID" };
this.addActionHandler(new Handler() {
#Override
public Action[] getActions(Object target, Object sender) {
return action;
}
#Override
public void handleAction(Action action, Object sender, Object target) {
switch(action.getCaption()) {
case "+New...": {
PmcUi.getCurrent().addWindow(taskWindow.createWindow());
break;
}
}
//what to do for action
}
});
this.addItemClickListener(new ItemClickListener(){
#Override
public void itemClick(ItemClickEvent event) {
if (event.isDoubleClick()) {
PmcUi.getCurrent().addWindow(detailsWindow.createWindow());
}
return;
}
});
return super.createTable();
}
#Override
protected IndexedContainer projectDatasource() {
IndexedContainer indexedContainer = new IndexedContainer();
for(String header: headers) {
indexedContainer.addContainerProperty(header, String.class, "");
}
List<Task> tasks = taskService.findAllTasks();
for(int i = 0; i < tasks.size(); i++) {
Object id = indexedContainer.addItem();
Task item = tasks.get(i);
indexedContainer.getContainerProperty(id, headers[0]).setValue(item.getDescription());
indexedContainer.getContainerProperty(id, headers[1]).setValue(item.getTaskProject());
indexedContainer.getContainerProperty(id, headers[2]).setValue(item.getStatus());
indexedContainer.getContainerProperty(id, headers[3]).setValue(item.getWeight());
indexedContainer.getContainerProperty(id, headers[4]).setValue(item.getTaskDeveloper());
indexedContainer.getContainerProperty(id, headers[5]).setValue(item.getTaskId());
}
return indexedContainer;
}
}
Where AbstractObjectTable
public abstract class AbstractObjectTable extends Table {
protected String caption;
protected String[] headers = null;
protected Table createTable() {
this.setContainerDataSource(projectDatasource());
this.setVisibleColumns(headers);
this.setSelectable(true);
this.setImmediate(true);
return this;
}
protected abstract IndexedContainer projectDatasource();
}
My +New... modal windows looks similar to that
#Component("newTaskWindow")
public class NewTaskWindow {
private Window createTaskWindow;
#Autowired
private TaskService taskService;
public Window createWindow() {
createTaskWindow = new Window("New Task");
initWindow();
fillWindow();
return createTaskWindow;
}
private void initWindow() {
createTaskWindow.setSizeUndefined();
createTaskWindow.setResizable(false);
createTaskWindow.setModal(true);
createTaskWindow.addCloseListener(new CloseListener() {
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField taskDescription = new TextField("Description");
final ComboBox taskProject = new ComboBox("Select project");
final ComboBox taskDeveloper = new ComboBox("Select developer");
final TextField taskWeight = new TextField("Task weight");
final TextField taskStatus = new TextField("Task status");
Button create = new Button("Create");
create.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Task task = new Task();
task.setTaskId(UUID.randomUUID().toString());
task.setStatus(taskStatus.getValue());
task.setTaskDeveloper(taskDeveloper.getValue().toString());
task.setTaskProject(taskProject.getValue().toString());
task.setWeight(taskWeight.getValue());
task.setDescription(taskDescription.getValue());
taskService.insertTask(task);
createTaskWindow.close();
}
});
Button close = new Button("Cancel");
close.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
createTaskWindow.close();
}
});
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(taskProject, taskDeveloper, taskWeight, taskStatus,
taskDescription, layout);
formLayout.setMargin(true);
createTaskWindow.setContent(formLayout);
}
}
And my details windows also have similar architecture.
#Component("showTaskDetailsWindow")
public class ShowTaskDetailsWindow {
private Window showDetailsWindow;
#Autowired
private TaskService taskService;
public Window createWindow() {
showDetailsWindow = new Window("Show details");
initWindow();
fillWindow();
return showDetailsWindow;
}
private void initWindow() {
showDetailsWindow.setSizeUndefined();
showDetailsWindow.setResizable(false);
showDetailsWindow.setModal(true);
showDetailsWindow.addCloseListener(new CloseListener() {
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField taskDescription = new TextField("Description");
final TextField taskProject = new TextField("Task project");
final TextField taskDeveloper = new TextField("Task developer");
final TextField taskWeight = new TextField("Task weight");
final TextField taskStatus = new TextField("Task status");
FormLayout formLayout = new FormLayout(taskProject, taskDeveloper, taskWeight, taskStatus, taskDescription);
formLayout.setMargin(true);
showDetailsWindow.setContent(formLayout);
}
}
What is the problem? Why it is continuously multiplying?
The problem is your getActions implementation
#Override
public Action[] getActions(Object target, Object sender) {
return new Action[] { new Action("+New...")};
}
You should create one instance of the "new Action("+New...")" item and store it for example in the TaskTable object.
The getActions(..) should alsways return the same instance.
If you always create a new action, it just adds them to the already existing actions.
Looks like the createTable() method of the TaskTable class is called too many times but the provided code doesn't show where that method is called. That causes that multiple action handlers and item click listeners are added to a table.

Categories