JavaFX TableView with complex TableColumn - tableview

Similar to the question here: Javafx tableview with data from multiple classes
I am trying to create a table composed of more than one class. The backend is sqlite tables and I am trying to implement the Toxi solution found here: http://tagging.pui.ch/post/37027745720/tags-database-schemas That is why my simplified example below is the way it is - split up into unnecessary classes.
It is the Tag column I am trying to figure out. Is a wrapper class the cleanest way to go,a BookmarkTag class? Can you setup event handlers to fill out this other column when the row is updated? Ultimately the cell will contain a fancier display of the tags (clickable icons).
Thank you.
My main class:
package complextableview;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ComplexTableView extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<Bookmark> bookmarks = FXCollections.observableArrayList();
ObservableList<Tag> tags = FXCollections.observableArrayList();
ObservableList<TagMap> tagMapList = FXCollections.observableArrayList();
TableView<Bookmark> bookmarkTV = new TableView<>(bookmarks);
TableColumn<Bookmark, String> bookmarkNameCol = new TableColumn<>("URL");
// What to put here?
//TableColumn<?, String> bookmarkTagCol = new TableColumn<>("Tags");
bookmarkNameCol.setCellValueFactory(new PropertyValueFactory<>("Name"));
// What to put here?
//bookmarkTagCol.setCellValueFactory(new PropertyValueFactory<>("?"));
bookmarkTV.getColumns().add(bookmarkNameCol);
//bookmarkTV.getColumns().add(bookmarkTagCol);
// Populate data
bookmarks.add(new Bookmark(0, "stackoverflow.com"));
bookmarks.add(new Bookmark(1, "reddit.com"));
tags.add(new Tag(0, "work"));
tags.add(new Tag(1, "home"));
tags.add(new Tag(2, "fun"));
tagMapList.add(new TagMap(0, 0, 0)); // 1st mapping = google & "work"
tagMapList.add(new TagMap(1, 1, 1)); // 2nd mapping = reddit & "home"
tagMapList.add(new TagMap(2, 1, 2)); // 3rd mapping = reddit & "fun"
// Table Output
// URL Tags
// stackoverflow.com 'work'
// reddit.com 'home,fun'
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for ( TagMap tagMapping : tagMapList ){
if ( tagMapping.getBookmarkId() == 1)
sb1.append(tags.get(tagMapping.getTagId()).getString());
else
sb2.append(tags.get(tagMapping.getTagId()).getString());
}
System.out.println(sb1.toString());
System.out.println(sb2.toString());
StackPane root = new StackPane();
root.getChildren().add(bookmarkTV);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("ComplexTableView");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And the other classes:
Bookmark
package complextableview;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Bookmark {
private final StringProperty name;
private final IntegerProperty id;
public Bookmark() {
id = new SimpleIntegerProperty(this, "id", 0);
name = new SimpleStringProperty(this, "name", "");
}
public Bookmark(Integer id, String name) {
this();
setId(id);
setName(name);
}
public final Integer getId() {return id.get();}
public final void setId(Integer value) {id.set(value);}
public IntegerProperty getIdProperty() {return id;}
public final String getName() {return name.get();}
public final void setName(String value) {name.set(value);}
public StringProperty getNameProperty() {return name;}
}
Tag
package complextableview;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Tag {
public final StringProperty string;
public final IntegerProperty id;
public Tag() {
id = new SimpleIntegerProperty(this, "id", 0);
string = new SimpleStringProperty(this, "string", "");
}
public Tag(Integer id, String string) {
this();
setId(id);
setString(string);
}
public final Integer getId() {return id.get();}
public final void setId(Integer value) {id.set(value);}
public IntegerProperty idProperty() {return id;}
public final String getString() {return string.get();}
public final void setString(String value) {string.set(value);}
public StringProperty stringProperty() {return string;}
}
TagMap
package complextableview;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class TagMap {
public final IntegerProperty id;
public final IntegerProperty bookmarkId;
public final IntegerProperty tagId;
public TagMap(){
id = new SimpleIntegerProperty(this, "id", 0);
bookmarkId = new SimpleIntegerProperty(this, "bookmarkId", 0);
tagId = new SimpleIntegerProperty(this, "tagId", 0);
}
public TagMap(Integer id, Integer bmId, Integer tagId) {
this();
setId(id);
setBookmarkId(bmId);
setTagId(tagId);
}
public final Integer getId() {return id.get();}
public final void setId(Integer value) {id.set(value);}
public IntegerProperty idProperty() {return id;}
public final Integer getTagId() {return tagId.get();}
public final void setTagId(Integer value) {tagId.set(value);}
public IntegerProperty tagIdProperty() {return tagId;}
public final Integer getBookmarkId() {return bookmarkId.get();}
public final void setBookmarkId(Integer value) {bookmarkId.set(value);}
public IntegerProperty bookmarkIdProperty() {return bookmarkId;}
}

Related

JavaFx: I need something similar to this but for a TableView instead of a ListView

I took a stab at this yesterday but the TableView documentation has me a bit confused. After working on it for a couple of hours I gave up. Just wondering if any javafx experts out there can help me with this. I want to update a TableView in a background thread periodically when items in my database change.
Rather than post my entire application I have tried to break it down to a simple example. Replace all occurrences of ListView with TableView and ....
Then what?
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* An example of triggering a JavaFX ListView when an item is modified.
*
* Displays a list of strings. It iterates through the strings adding
* exclamation marks with 2 second pauses in between. Each modification is
* accompanied by firing an event to indicate to the ListView that the value
* has been modified.
*
* #author Mark Fashing
*/
public class ListViewTest extends Application {
/**
* Informs the ListView that one of its items has been modified.
*
* #param listView The ListView to trigger.
* #param newValue The new value of the list item that changed.
* #param i The index of the list item that changed.
*/
public static <T> void triggerUpdate(ListView<T> listView, T newValue, int i) {
EventType<? extends ListView.EditEvent<T>> type = ListView.editCommitEvent();
Event event = new ListView.EditEvent<>(listView, type, newValue, i);
listView.fireEvent(event);
}
#Override
public void start(Stage primaryStage) {
// Create a list of mutable data. StringBuffer works nicely.
final List<StringBuffer> listData = Stream.of("Fee", "Fi", "Fo", "Fum")
.map(StringBuffer::new)
.collect(Collectors.toList());
final ListView<StringBuffer> listView = new ListView<>();
listView.getItems().addAll(listData);
final StackPane root = new StackPane();
root.getChildren().add(listView);
primaryStage.setScene(new Scene(root));
primaryStage.show();
// Modify an item in the list every 2 seconds.
new Thread(() -> {
IntStream.range(0, listData.size()).forEach(i -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(listData.get(i));
Platform.runLater(() -> {
// Where the magic happens.
listData.get(i).append("!");
triggerUpdate(listView, listData.get(i), i);
});
});
}).start();
}
public static void main(String[] args) {
launch(args);
}
}
Here is my first attempt:
Create a person class....
package org.pauquette.example;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleStringProperty email;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getEmail() {
return email.get();
}
public String getFirstName() {
return firstName.get();
}
public String getLastName() {
return lastName.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public void setLastName(String fName) {
lastName.set(fName);
}
}
Create an extremely simple model class...
package org.pauquette.example;
import javafx.collections.*;
public class PeopleModel {
private ObservableList<Person> people=FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
public ObservableList<Person> getPeople() {
return people;
}
}
Now create a TableView of just firstName and build the columns then update the firstName every 2 seconds.......
package org.pauquette.example;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* An example of triggering a JavaFX TableView when an item is modified.
*
* Displays a list of strings. It iterates through the strings adding
* exclamation marks with 2 second pauses in between. Each modification is
* accompanied by firing an event to indicate to the TableView that the value
* has been modified.
*
* #author Mark Fashing-Modified for TableView by Bryan Pauquette
*/
public class TableViewTest extends Application {
/*
public static <T> void triggerUpdate(TableView<T> listView, T newValue, int i) {
EventType<? extends TableView.EditEvent<T>> type = TableView.editCommitEvent();
Event event = new TableView.EditEvent<>(listView, type, newValue, i);
listView.fireEvent(event);
}*/
#Override
public void start(Stage primaryStage) {
final TableView<Person> listView = new TableView<Person>();
final PeopleModel model=new PeopleModel();
final ObservableList<Person> listData=model.getPeople();
listView.getItems().addAll(listData);
final StackPane root = new StackPane();
buildColumns(listView,listData);
root.getChildren().add(listView);
primaryStage.setScene(new Scene(root));
primaryStage.show();
// Modify an item in the list every 2 seconds.
new Thread(() -> {
IntStream.range(0, listData.size()).forEach(i -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(listData.get(i));
Platform.runLater(() -> {
// Where the magic happens.
Person p=listData.get(i);
p.setFirstName(new StringBuilder(p.getFirstName()).append("!").toString());
//triggerUpdate(listView, listData.get(i), i);
});
});
}).start();
}
private void buildColumns(TableView<Person> listView,ObservableList<Person> listData) {
TableColumn<Person, String> dataCol = new TableColumn<Person, String>("First Name");
dataCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
listView.getColumns().add(dataCol);
listView.setItems(listData);
}
public static void main(String[] args) {
launch(args);
}
}
The method I am struggling with is triggerUpdate.....
I want the firstName column to get updated in the view with an appended exclamation point every 2 seconds just like in the original simple list view.
Here is working code.....
package org.pauquette.example;
import java.util.Observable;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
public class Person extends Observable {
private final SimpleStringProperty email;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getEmail() {
return email.get();
}
public String getFirstName() {
return firstName.get();
}
public String getLastName() {
return lastName.get();
}
public void setEmail(String emailIn) {
email.setValue(emailIn);
setChanged();
notifyObservers(email);
}
public void setFirstName(String fNameIn) {
firstName.setValue(fNameIn);
setChanged();
notifyObservers(firstName);
}
public void setLastName(String lNameIn) {
lastName.setValue(lNameIn);
setChanged();
notifyObservers();
}
public ObservableValue<String> firstNameProperty() {
return firstName;
}
}
And......
package org.pauquette.example;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
* An example of triggering a JavaFX TableView when an item is modified.
*
* Displays a list of strings. It iterates through the strings adding
* exclamation marks with 2 second pauses in between. Each modification is
* accompanied by firing an event to indicate to the TableView that the value
* has been modified.
*
* #author Mark Fashing-Modified for TableView by Bryan Pauquette
*/
public class TableViewTest extends Application {
/*
public static <T> void triggerUpdate(TableView<T> listView, T newValue, int i) {
EventType<? extends TableView.EditEvent<T>> type = TableView.editCommitEvent();
Event event = new TableView.EditEvent<>(listView, type, newValue, i);
listView.fireEvent(event);
}*/
#Override
public void start(Stage primaryStage) {
final TableView<Person> listView = new TableView<Person>();
final PeopleModel model=new PeopleModel();
final ObservableList<Person> listData=model.getPeople();
/* int row=0;
for (Person p : listData) {
p.addObserver(new PersonObserver(listView,row));
row++;
}*/
listView.getItems().addAll(listData);
final StackPane root = new StackPane();
buildColumns(listView,listData);
root.getChildren().add(listView);
primaryStage.setScene(new Scene(root));
primaryStage.show();
// Modify an item in the list every 2 seconds.
new Thread(() -> {
IntStream.range(0, listData.size()).forEach(i -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(listData.get(i));
Platform.runLater(() -> {
// Where the magic happens.
Person p=listData.get(i);
p.setFirstName(new StringBuilder(p.getFirstName()).append("!").toString());
//triggerUpdate(listView, listData.get(i), i);
});
});
}).start();
}
private void buildColumns(TableView<Person> listView,ObservableList<Person> listData) {
TableColumn<Person, String> dataCol = new TableColumn<Person, String>("First Name");
//dataCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
dataCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
// p.getValue() returns the Person instance for a particular TableView row
return p.getValue().firstNameProperty();
}
});
listView.getColumns().add(dataCol);
listView.setItems(listData);
}
public static void main(String[] args) {
launch(args);
}
}

mouseevent not working in java fx [duplicate]

I am trying to make a tableview clickable, where it will return the text in the cell that is clicked. I am receiving two errors when trying to compile in Netbeans. All of the code was taken from "Example 12-11: Alternative Solution Of Cell Editing" official tableview tutorial & from this stackoverflow.com answer. Here are the errors:
type argument MouseEvent is not within bounds of type-variable T
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() {
where T is a type-variable:
T extends Event declared in interface EventHandler
method addEventFilter in class Node cannot be applied to given types;
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() {
required: EventType,EventHandler
found: int,>
reason: no instance(s) of type variable(s) T exist so that argument type int conforms to formal parameter type EventType
where T is a type-variable:
T extends Event declared in method addEventFilter(EventType,EventHandler)
import java.awt.event.MouseEvent;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(false);
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<Person, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Cell text: " + c.getText());
}
}
});
return cell;
}
};
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(cellFactory);
firstNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFirstName(t.getNewValue());
}
}
);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(cellFactory);
lastNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue());
}
}
);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setEmail(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}
}
You have the wrong import for MouseEvent. You need javafx.scene.input.MouseEvent, not the AWT version.
It is likely that when your IDE prompted you to import MouseEvent, the first option was java.awt.event.MouseEvent, rather than javafx.scene.input.MouseEvent.
Be careful while importing with JavaFX, as many of it's classes have the same names as classes in standard Java libraries.

Deleting Data From Database Through JavaFX GUI

What I want to be able to do is load my database through a table view select an item and have it deleted into the database. I do not have users entering the id of a particular song so it makes it harder for me to accomplish this. I have the GUI set up and all the code that I have so far.
GUI CODE:
SongContent Code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package playmymusic;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author man
*/
public class SongContent
{
private final StringProperty artist;
private final StringProperty title;
private final StringProperty genre;
private final IntegerProperty id;
public SongContent(int id, String artist, String title, String genre)
{
this.artist = new SimpleStringProperty(artist);
this.title = new SimpleStringProperty(title);
this.genre = new SimpleStringProperty(genre);
this.id = new SimpleIntegerProperty(id);
}
public Integer getId()
{
return id.get();
}
public void setID(int paramId)
{
id.set(paramId);
}
public String getArtist()
{
return artist.get();
}
public void setArtist(String paramArtist)
{
artist.set(paramArtist);
}
public String getTitle()
{
return title.get();
}
public void setTitle(String paramTitle)
{
title.set(paramTitle);
}
public String getGenre()
{
return genre.get();
}
public void setGenre(String paramGenre)
{
genre.set(paramGenre);
}
public StringProperty artistProperty(){return artist;}
public StringProperty titleProperty(){return title;}
public StringProperty genreProperty(){return genre;}
public IntegerProperty idProperty() { return id;}
}
Controller Code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package playmymusic;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;
import javafx.beans.property.IntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javax.swing.JOptionPane;
import org.apache.derby.jdbc.ClientDriver;
/**
*
* #author man
*/
public class FXMLDocumentController implements Initializable {
public LoginModel loginModel = new LoginModel();
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private TextField txtArtist;
#FXML
private TextField fxTitle;
#FXML
private TextField fxGenre;
#FXML
private TableView<SongContent> tableView;
#FXML
private TableColumn<SongContent, Integer> id;
#FXML
private TableColumn<SongContent, String> artist;
#FXML
private TableColumn<SongContent, String> title;
#FXML
private TableColumn<SongContent, String> genre;
private ObservableList<SongContent> data;
#FXML
private void Login(ActionEvent event) throws SQLException {
try {
if(loginModel.isLogin(txtUsername.getText(), txtPassword.getText()))
{
Stage primaryStage = new Stage();
FXMLLoader loader = new FXMLLoader();
Pane root = loader.load(getClass().getResource("PopUpWindow.fxml").openStream());
Scene scene = new Scene(root, 785, 809);
primaryStage.setScene(scene);
primaryStage.show();
PlayMyMusic.primaryStage.close();
}else
{
System.out.println("WOOPS");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#FXML
private void songs(ActionEvent e) throws SQLException, ClassNotFoundException
{
loginModel.insertSongs(txtArtist.getText(), fxTitle.getText(), fxGenre.getText());
try
{
int i = 1;
Connection conn = DriverManager.getConnection("jdbc:derby://localhost:1527/PlayMyMusicDB;user=test;password=test");
data = FXCollections.observableArrayList();
ResultSet rs = conn.createStatement().executeQuery("select * from Song");
while(rs.next())
{
data.add(new SongContent(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4)));
i++;
}
}catch(SQLException ex) {
System.err.println("Error" + ex);
}
id.setCellValueFactory(new PropertyValueFactory<>("id"));
artist.setCellValueFactory(new PropertyValueFactory<>("artist"));
title.setCellValueFactory(new PropertyValueFactory<>("title"));
genre.setCellValueFactory(new PropertyValueFactory<>("genre"));
tableView.setItems(null);
tableView.setItems(data);
txtArtist.clear();
fxTitle.clear();
fxGenre.clear();
}
#FXML
public void deleteItems(ActionEvent e) throws SQLException, ClassNotFoundException
{
Connection c = DriverManager.getConnection("jdbc:derby://localhost:1527/PlayMyMusicDB;user=test;password=test");
int action = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this item?");
if(action == 0)
{
try
{
IntegerProperty i = SongContent.idProperty();
ResultSet rs = c.createStatement().executeQuery("DELETE FROM Song where i = " + i);
}catch(Exception e1)
{
e1.printStackTrace();
}
}
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
}
}
`
Any explination of why this could not be deleting my data? I would also love it if someone explained to me a strategy of resetting the SongNumberID every time the GUI opened and closed. But, the main goal for me is to figure out how to delete songs.
Thanks so much
-Aaron
The result of calling toString on a SimpleIntegerProperty is something like IntegerProperty [value: 10]. You should use the value, not the IntegerProperty. Furthermore it's better to use a PreparedStatement to create the query. Also you should get the selected item from the table instead of trying to reference a instance method as if it was static:
SongContent song = tableView.getSelectionModel().getSelectedItem();
if (song != null) {
// there is a selection -> delete
...
PreparedStatement statement = c.prepareStatement("DELETE FROM Song WHERE i = ?");
statement.setInt(1, song.getId());
statement.executeUpdate();
...
}
Furthermore you should make sure i is actually the column name of the id column (and not id).

Can't stop javafx tables from ignoring my the setter function validation

I'm using javafx to do some table stuff. I want to validate my textfields in the myTextRow Class. In the "setText2" method I check the input if it is not bigger than 6 symbols, but it has no effects at all.
import java.util.ArrayList;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
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.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Supermain extends Application {
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory());
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory());
//Add data
final ObservableList<myTextRow> data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
public TextFieldCell() {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
this.setGraphic(textField);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
if(text2check(text)){
this.text2.set(text);}
else
{System.out.println("wrong value!!!");}
}
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public StringProperty textProperty() {
return text;
}
public String getText2() {
return text2.get();
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
public boolean text2check(String t)
{
if(t.length()>6)return false;
return true;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
// System.out.println("dubbyloop.FXMLDocumentController.get_text_width(): "+l.prefWidth(-1));
return l.prefWidth(-1);
}
}
A rule of the JavaFX Properties pattern is that for a property x, invoking xProperty().setValue(value) should always be identical to invoking setX(value). Your validation makes this not true. The binding your cell implementation uses invokes the setValue method on the property, which is why it bypasses your validation check.
(Side note: in all the code I am going to change the names so that they adhere to proper naming conventions.)
The default way to implement a property in this pattern is:
public class MyTextRow {
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text ;
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
}
By having the set/get methods delegate to the appropriate property methods, you are guaranteed these rules are enforced, even if the textProperty() methods is overridden in a subclass. Making the set and get methods final ensures that the rule is not broken by a subclass overriding those methods.
One approach might be to override the set and setValue methods in the property, as follows:
public class MyTextRow {
private final StringProperty text2 = new StringPropertyBase() {
#Override
public String getName() {
return "text2";
}
#Override
public Object getBean() {
return MyTextRow.this ;
}
#Override
public void setValue(String value) {
if (text2Check(value)) {
super.setValue(value);
}
}
#Override
public void set(String value) {
if (text2Check(value)) {
super.set(value);
}
}
}
public StringProperty text2Property() {
return text2 ;
}
public final void setText2(String text2) {
text2Property().set(text2);
}
public final String getText2() {
return text2Property().get();
}
// ...
}
however, I think this will break the bidirectional binding that you have with the text property in the TextArea (basically, there is no way to communicate back to the text area when a change is vetoed, so the text area will not know to revert to the previous value). One fix would be to implement your cell using listeners on the properties instead of bindings. You could use a TextFormatter on the text area that simply updates the property and vetoes the text change if the change doesn't occur.
Here is a complete SSCCE using this approach:
import java.util.function.Function;
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.stage.Stage;
public class VetoStringChange extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Description", Item::descriptionProperty));
for (int i = 1; i <= 20 ; i++) {
table.getItems().add(new Item("Item "+i, ""));
}
primaryStage.setScene(new Scene(table, 600, 600));
primaryStage.show();
}
public static <S> TableColumn<S,String> column(String title, Function<S,Property<String>> property) {
TableColumn<S,String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(tc -> new TextAreaCell<S>(property));
col.setPrefWidth(200);
return col ;
}
public static class TextAreaCell<S> extends TableCell<S, String> {
private TextArea textArea ;
public TextAreaCell(Function<S, Property<String>> propertyAccessor) {
textArea = new TextArea();
textArea.setWrapText(true);
textArea.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textArea.setMaxHeight(Double.MAX_VALUE);
UnaryOperator<Change> filter = c -> {
String proposedText = c.getControlNewText() ;
Property<String> prop = propertyAccessor.apply(getTableView().getItems().get(getIndex()));
prop.setValue(proposedText);
if (prop.getValue().equals(proposedText)) {
return c ;
} else {
return null ;
}
};
textArea.setTextFormatter(new TextFormatter<String>(filter));
this.setGraphic(textArea);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
if (! textArea.getText().equals(item)) {
textArea.setText(item);
}
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
public static class Item {
private final StringProperty name = new StringPropertyBase() {
#Override
public Object getBean() {
return Item.this;
}
#Override
public String getName() {
return "name" ;
}
#Override
public void set(String value) {
if (checkValue(value)) {
super.set(value);
}
}
#Override
public void setValue(String value) {
if (checkValue(value)) {
super.setValue(value);
}
}
};
private final StringProperty description = new SimpleStringProperty();
public Item(String name, String description) {
setName(name);
setDescription(description);
}
private boolean checkValue(String value) {
return value.length() <= 6 ;
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final StringProperty descriptionProperty() {
return this.description;
}
public final String getDescription() {
return this.descriptionProperty().get();
}
public final void setDescription(final String description) {
this.descriptionProperty().set(description);
}
}
public static void main(String[] args) {
launch(args);
}
}
Another approach is to allow a "commit and revert" type strategy on your property:
public class MyTextRow {
private final StringProperty text2 = new SimpleStringProperty();
public MyTextRow() {
text2.addListener((obs, oldText, newText) -> {
if (! checkText2(newText)) {
// sanity check:
if (checkText2(oldText)) {
text2.set(oldText);
}
}
});
}
public StringProperty text2Property() {
return text ;
}
public final void setText2(String text2) {
text2Property().set(text2);
}
public final String getText2() {
return text2Property().get();
}
}
In general I dislike validation by listening for an invalid value and reverting like this, because other listeners to the property will see all the changes, including changes to and from invalid values. However, this might be the best option in this case.
Finally, you could consider vetoing invalid changes as in the first option, and also setting a TextFormatter on the control in the cell that simply doesn't allow text entry that results in an invalid string. This isn't always possible from a usability perspective (e.g. if empty strings are invalid, you almost always want to allow the user to temporarily delete all the text), and it means keeping two validation checks in sync in your code, which is a pain.

How to populate TableView data on the other screen TextField in JavaFX 2.0

I am having problem in populating data from a table in one screen to a Text Field in the other screen. I have two classes FirstClass containing a textbox and a button. On pressing a button a second window is opened containing a Table of values. As the user double clicks a row the value of the second column of the row should be inserted into the textbox of the FirstClass. Code of both the classes is attached. Thanking you in anticipation.
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 FirstClass extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("First Class");
GridPane gridpane = new GridPane();
gridpane.setPadding(new Insets(5));
gridpane.setHgap(5);
gridpane.setVgap(5);
final TextField userNameFld = new TextField();
gridpane.add(userNameFld, 1, 1);
Button btn = new Button();
btn.setText("Show Table");
gridpane.add(btn, 1, 3);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
String a = TableClass.showDialog(primaryStage, true, "Table Window" );
userNameFld.setText(a);
}
});
StackPane root = new StackPane();
Scene scene =new Scene(root, 300, 250);
root.getChildren().addAll(gridpane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class TableClass extends Stage {
private static TableClass dialog;
private static String value = "";
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private Person(String fName, String lName) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
}
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("JACK", "BROWN"),
new Person("JOHN", "VIANNEYS"),
new Person("MICHAEL", "NELSON"),
new Person("WILLIAM", " CAREY")
);
public TableClass(Stage owner, boolean modality, String title) {
super();
initOwner(owner);
Modality m = modality ? Modality.APPLICATION_MODAL : Modality.NONE;
initModality(m);
setOpacity(1);
setTitle(title);
StackPane root = new StackPane();
Scene scene = new Scene(root, 750, 750);
setScene(scene);
GridPane gridpane = new GridPane();
gridpane.setPadding(new Insets(5));
gridpane.setHgap(5);
gridpane.setVgap(5);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person,String>("firstName")
);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(200);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person,String>("lastName")
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol);
table.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
if (me.getClickCount() >= 2) {
String srr = table.getItems().get (table.getSelectionModel().getSelectedIndex()).getLastName();
value = srr;
dialog.hide();
}
}
});
gridpane.add(table, 1, 5,1,20 );
root.getChildren().add(gridpane);
}
public static String showDialog(Stage stg, Boolean a , String title){
dialog = new TableClass( stg,a, title);
dialog.show();
return value;
}
}
The quick and easy way (but it introduces coupling between the 2 classes) would be to pass userNameFld to your showDialog method and make it a member of TableClass. You can then change its value from the TableClass class.
A better way would be to bind the value of userNameFld to value.

Resources