So I am trying to make a simple JavaFX program that displays a few tables of data. I have my model view controller, and from what I can tell everything looks clear. Running the application shows no problems except that no data shows up in the tables. The 4 tables are initialized, I can click on them so clearly its setting them up, just no data is being put inside them. Here is my code.
package releaseData;
import java.io.IOException;
import java.util.Observable;
import releaseData.model.ReleaseData;
import releaseData.view.ShowReleaseDataController;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainApp extends Application {
private Stage primaryStage;
//must match the the type of pane in .fxml file
private AnchorPane rootLayout;
//set up a list
private ObservableList<ReleaseData> releaseData = FXCollections.observableArrayList();
//Constructor
public MainApp()
{
releaseData.add(new ReleaseData("Borderlands", "2K", "11/24/2010", "4/5"));
releaseData.add(new ReleaseData("Half-Life 2", "Valve", "9/9/2002", "5/5"));
releaseData.add(new ReleaseData("Far Cry 3", "Activision", "12/1/2012", "5/5"));
releaseData.add(new ReleaseData("Goat Simulator", "Coffe-Stain", "8/1/2014", "3/5"));
}
/**
#return
*/
public ObservableList<ReleaseData> getReleaseData()
{
return releaseData;
}
#Override
public void start(Stage primaryStage)
{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Game Data");
initRootLayout();
}
public void initRootLayout()
{
try {
//Load fxml layout
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/releaseDataOverview.fxml"));
rootLayout = (AnchorPane) loader.load();
//Give the controller access
ShowReleaseDataController controller = loader.getController();
controller.setMainApp(this);
//Show scene containing the root layout
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
}catch (IOException e) {e.printStackTrace(); }
}
public Stage getPrimaryStage()
{
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
package releaseData.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class ReleaseData {
private final StringProperty name;
private final StringProperty publisher;
private final StringProperty date;
private final StringProperty rating;
//Constructors
public ReleaseData() { this(null, null, null, null); }
public ReleaseData(String name, String publisher, String date, String rating){
this.name = new SimpleStringProperty(name);
this.publisher = new SimpleStringProperty(publisher);
this.date = new SimpleStringProperty(date);
this.rating = new SimpleStringProperty(rating);
}
//Sets, gets, properties
public String getName() { return name.get(); }
public void setName(String name) {this.name.set(name); }
public StringProperty nameProperty() { return name; }
public String getPublisher() {return publisher.get(); }
public void setPublisher(String publisher) {this.publisher.set(publisher); }
public StringProperty publisherProperty() {return publisher; }
public String getDate() { return date.get(); }
public void setDate(String date) {this.date.set(date); }
public StringProperty dateProperty() { return date; }
public String getRating() { return rating.get(); }
public void setRating(String rating) { this.rating.set(rating); }
public StringProperty ratingProperty() {return rating; }
}
package releaseData.view;
import releaseData.MainApp;
import releaseData.model.ReleaseData;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
public class ShowReleaseDataController {
#FXML
private TableView<ReleaseData> releaseDataTable;
#FXML
private TableColumn<ReleaseData, String> nameColumn;
#FXML
private TableColumn<ReleaseData, String> publisherColumn;
#FXML
private TableColumn<ReleaseData, String> dateColumn;
#FXML
private TableColumn<ReleaseData, String> ratingColumn;
private MainApp mainApp;
public ShowReleaseDataController() {}
#FXML
private void initializer()
{
nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
publisherColumn.setCellValueFactory(cellData -> cellData.getValue().publisherProperty());
dateColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
ratingColumn.setCellValueFactory(cellData -> cellData.getValue().ratingProperty());
}
public void setMainApp(MainApp mainApp)
{
this.mainApp=mainApp;
releaseDataTable.setItems(mainApp.getReleaseData());
}
}
I've done about everything I can think of to find the issue. If someone could point me in the right direction I would be extremely grateful!
The initialisation method should be
public void initialize() {...}
not initializer()
Related
we want to create a combobox within a table. Each combobox should have different options based on the row. In our example we have a table with different server ip addresses and a combobox with all the users on that server.
We have no idea how to populate the different options to the combobox inside the CellFactory and also we are not sure how to bind the comboxbox entry with the corresponding model.
We created an example, maybe someone has ideas:
CredentialModel
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class CredentialModel {
private StringProperty username = new SimpleStringProperty();
private StringProperty password = new SimpleStringProperty();
public CredentialModel(String username, String password) {
setUsername(username);
setPassword(password);
}
public String getUsername() {
return username.get();
}
public StringProperty usernameProperty() {
return username;
}
public void setUsername(String username) {
this.username.set(username);
}
public String getPassword() {
return password.get();
}
public StringProperty passwordProperty() {
return password;
}
public void setPassword(String password) {
this.password.set(password);
}
}
ServerModel
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ServerModel {
private IntegerProperty id = new SimpleIntegerProperty();
private StringProperty ip = new SimpleStringProperty();
private ObjectProperty<CredentialModel> credential = new SimpleObjectProperty<>();
private ObservableList<CredentialModel> allCredentials = FXCollections.observableArrayList();
public ObservableList<CredentialModel> getAllCredentials() {
return allCredentials;
}
public void setAllCredentials(ObservableList<CredentialModel> allCredentials) {
this.allCredentials = allCredentials;
}
public ServerModel(Integer id, String ip) {
setId(id);
setIp(ip);
}
public String getIp() {
return ip.get();
}
public StringProperty ipProperty() {
return ip;
}
public void setIp(String ip) {
this.ip.set(ip);
}
public CredentialModel getCredential() {
return credential.get();
}
public ObjectProperty<CredentialModel> credentialProperty() {
return credential;
}
public void setCredential(CredentialModel credential) {
this.credential.set(credential);
}
public int getId() {
return id.get();
}
public IntegerProperty idProperty() {
return id;
}
public void setId(int id) {
this.id.set(id);
}
}
And the main application TableApplication
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableApplication extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
VBox box = new VBox();
TableView<ServerModel> tableView = new TableView<>();
TableColumn<ServerModel, String> ipColumn = new TableColumn<>("IP-Address");
TableColumn<ServerModel, CredentialModel> userColumn = new TableColumn<>("Username");
tableView.getColumns().addAll(ipColumn, userColumn);
ipColumn.setCellValueFactory(param -> param.getValue().ipProperty());
tableView.getItems().addAll(populateTable());
userColumn.setCellFactory(new Callback<TableColumn<ServerModel,CredentialModel>, TableCell<ServerModel,CredentialModel>>() {
#Override
public TableCell<ServerModel, CredentialModel> call(TableColumn<ServerModel, CredentialModel> param) {
return new UserComboBoxTableCell();
}
});
Button button = new Button("Start");
button.setOnAction(event -> System.out.println(tableView.getSelectionModel().getSelectedItem().getCredential().getUsername()));
box.getChildren().addAll(tableView, button);
Scene scene = new Scene(box,800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private ObservableList<ServerModel> populateTable() {
ServerModel serverModel1 = new ServerModel(1, "192.168.0.1");
serverModel1.setAllCredentials(FXCollections.observableArrayList(new CredentialModel("user1", "pw1"), new CredentialModel("user2", "pw2")));
ServerModel serverModel2 = new ServerModel(2, "192.168.0.2");
serverModel2.setAllCredentials(FXCollections.observableArrayList(new CredentialModel("user3", "pw3")));
return FXCollections.observableArrayList(serverModel1, serverModel2);
}
And the TableCellClass UserComboBoxTableCell
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableCell;
public class UserComboBoxTableCell extends TableCell<ServerModel, CredentialModel> {
public UserComboBoxTableCell() {
}
#Override
protected void updateItem(CredentialModel item, boolean empty) {
super.updateItem(item, empty);
if(empty == false) {
if(getTableRow() != null && getTableView() != null) {
int index = getTableRow().getIndex();
ServerModel serverModel = getTableView().getItems().get(index);
ComboBox<CredentialModel> box = new ComboBox<>(serverModel.getAllCredentials());
box.valueProperty().bindBidirectional(serverModel.credentialProperty());
setGraphic(box);
}
}
}
}
The idea of the methode loadCredentials is that for each row this methode should be called for "dummy"-loading the credentials for the server row.
The idea of the onAction method of the button is to display the selected username from the selected serverrow. In the moment we just print the ip address because the credential is always null (because it is not binded in the moment).
Thanks for your help.
Hauke
EDIT
I just added an observable list of credential models to the server and populated the list view differently. But still I am not sure how to set the options to the combobox within the CellFactory because I need to access the row and I am not sure about the binding.
EDIT2
Now it is working. I updated the examples above.
If this is a duplicate, my apologies, but I wasn't able to find an answer quite yet.
I am working on a project to run a basic inventory management system in JavaFX. I am currently trying to allow the user to add new items into a SQLite database, and while the ID and item name gets added into the database just fine, the initial value that is entered into a textfield seems to be ignored.
The program instead puts a value of 0.0 in the units available field. Here is my current code:
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage){
try {
Parent root = FXMLLoader.load(getClass().getResource("DatabaseView.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("Brewer's Guide Inventory Management");
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
beerDatabaseData.java
package sample;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class beerDatabaseData {
private final StringProperty ID;
private final StringProperty name;
private DoubleProperty units;
public beerDatabaseData (String ID, String Name, Double Units)
{
this.ID = new SimpleStringProperty(ID);
this.name = new SimpleStringProperty(Name);
this.units = new SimpleDoubleProperty(Units);
}
public StringProperty IDProperty()
{
return this.ID;
}
public String getID()
{
return this.IDProperty().get();
}
public void setID(final String ID)
{
this.IDProperty().set(ID);
}
public StringProperty nameProperty()
{
return this.name;
}
public String getName()
{
return this.nameProperty().get();
}
public void setName(final String name)
{
this.nameProperty().set(name);
}
public DoubleProperty unitsProperty()
{
return this.units;
}
public Double getUnits()
{
return this.unitsProperty().get();
}
public void setUnits(Double units)
{
this.unitsProperty().set(units);
}
}
DatabaseViewController
package sample;
/*
Add method to auto-load data rather than pressing button (possible on-load).
Look at dependency injection (inversion of control (if possible) for testing).
Malt service (all actions with malt data).
Separation of concerns.
*/
//https://stackoverflow.com/questions/50358299/javafx-populating-multiple-tables-in-separate-tabs
//https://stackoverflow.com/questions/41465181/tableview-update-database-on-edit
//https://stackoverflow.com/questions/45977390/how-to-force-a-double-input-in-a-textfield-in-javafx
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;
public class DatabaseViewController {
#FXML
private TextField idNumberInput1, idNumberInput2;
#FXML
private TextField nameInput1, nameInput2;
#FXML
private TextField unitsInput1, unitsInput2;
#FXML
private TableView<beerDatabaseData> beerTab1, beerTab2;
#FXML
private TableColumn<beerDatabaseData, String> ID1, ID2;
#FXML
private TableColumn<beerDatabaseData, String> name1, name2;
#FXML
private TableColumn<beerDatabaseData, Double> units1, units2;
/*private ObservableList<DatabaseData> data;*/
public void initialize(URL location, ResourceBundle resource) {
//
}
private ObservableList<beerDatabaseData> data1;
private ObservableList<beerDatabaseData> data2;
public void LoadData(ActionEvent event) throws SQLException {
try {
Connection conn = SQLConnection.Connector();
this.data1 = FXCollections.observableArrayList();
this.data2 = FXCollections.observableArrayList();
ResultSet rs1 = conn.createStatement().executeQuery("SELECT * FROM Malts");
while (rs1.next()) {
this.data1.add(new beerDatabaseData(rs1.getString(1), rs1.getString(2), rs1.getDouble(3)));
}
ResultSet rs2 = conn.createStatement().executeQuery("SELECT * FROM HOPS");
while (rs2.next()) {
this.data2.add(new beerDatabaseData(rs2.getString(1), rs2.getString(2), rs2.getDouble(3)));
}
}catch (SQLException e) {
System.out.println(e);
}
this.ID1.setCellValueFactory(new PropertyValueFactory<beerDatabaseData, String>("ID"));
this.name1.setCellValueFactory(new PropertyValueFactory<beerDatabaseData, String>("Name"));
this.units1.setCellValueFactory(new PropertyValueFactory<beerDatabaseData, Double>("Units"));
this.beerTab1.setItems(null);
this.beerTab1.setItems(this.data1);
this.ID2.setCellValueFactory(new PropertyValueFactory<beerDatabaseData, String>("ID"));
this.name2.setCellValueFactory(new PropertyValueFactory<beerDatabaseData, String>("Name"));
this.units2.setCellValueFactory(new PropertyValueFactory<beerDatabaseData, Double>("Units"));
this.beerTab2.setItems(null);
this.beerTab2.setItems(this.data2);
}
//Separating add functions for each ingredient since things are a little different for each.
public void addMalt(ActionEvent event){
try
{
Connection conn = SQLConnection.Connector();
PreparedStatement Prs = conn.prepareStatement("INSERT INTO Malts(Malt_ID,Malt_Name, Malt_Units) VALUES(?, ?, ?)");
Prs.setString(1,this.idNumberInput1.getText());
Prs.setString(2, this.nameInput1.getText());
/* Pattern validEditingState = Pattern.compile("-?(([1-9][0-9]*)|0)?(\\.[0-9]*)?");
UnaryOperator<TextFormatter.Change> filter = c -> {
String text = c.getControlNewText();
if(validEditingState.matcher(text).matches()){
return c;
}
else{
return null;
}
};
StringConverter<Double> converter = new StringConverter<Double>() {
#Override
public Double fromString(String s) {
if (s.isEmpty() || "-".equals(s) || ".".equals(s) || "-.".equals(s)){
return 0.0;
} else{
return Double.valueOf(s);
}
}
#Override
public String toString(Double d) {
return d.toString();
}
};
TextFormatter<Double> textFormatter = new TextFormatter<>(converter, 0.0, filter);
unitsInput1.setTextFormatter(textFormatter);
*/
Prs.setString(3, this.unitsInput1.getText());
Prs.execute();
conn.close();
}
catch(SQLException e)
{
System.out.println(e);
}
}
/* public void addHops(ActionEvent event){
try
{
Connection conn = SQLConnection.Connector();
}
catch(SQLException e){
System.out.println(e);
}
}*/
}
I imagine there's a glaring issue that is causing this, but I'm not seeing it. What do I need to change or include to make this work? Thank you in advanced.
Looks like the Malt_Units column in your table is numeric, but you try to insert it as a string. You should parse the value in your input and then set it as Double:
Prs.setDouble(3, Double.parseDouble(this.unitsInput1.getText()));
I would like to change the color of tableview based on some condition, i figured out how to do this, table is changing color as wanted but the only problem that im getting is that the value of table is not showing, i tried to system.out.println the value of the cell it is returning null, i wonder why ??
here is java 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 gis_map;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.AnchorPane;
import javafx.collections.FXCollections;
import javafx.scene.control.TableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.paint.Color;
/**
* FXML Controller class
*
* #author HP
*/
public class CitizenScheduledController implements Initializable {
#FXML
private AnchorPane root;
#FXML
public TableView<citizen> scheduledCitizenTable;
#FXML
private Button approveBtn;
#FXML
private TableColumn<citizen, Integer> idColumn;
#FXML
private TableColumn<citizen, String> nameColumn;
#FXML
private TableColumn<citizen, String> addressColumn;
#FXML
private TableColumn<citizen, String> arrivalColumn;
#FXML
private TableColumn<citizen, String> departureColumn;
#FXML
private TableColumn<citizen, String> vehicleId;
#FXML
private TableColumn<citizen, Integer> waste;
#FXML
private TableColumn<citizen, String> remarks;
VehicleRoutingProblem vpr = new VehicleRoutingProblem();
public static ObservableList<citizen> List = FXCollections.observableArrayList(
// new citizen(1,"testing","testing","testing","testing")
);
public ObservableList<citizen> getList() {
return List;
}
public void setList(ObservableList<citizen> List) {
this.List = List;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
//System.out.println("List contains"+List.size());
try{
vehicleId.setCellValueFactory(new PropertyValueFactory<citizen,String>("vehicleid"));
idColumn.setCellValueFactory(new PropertyValueFactory<citizen, Integer>("citizenId"));
nameColumn.setCellValueFactory(new PropertyValueFactory<citizen, String>("citizenName"));
addressColumn.setCellValueFactory(new PropertyValueFactory<citizen, String>("citizenAdress"));
arrivalColumn.setCellValueFactory(new PropertyValueFactory<citizen, String>("arrivalTime"));
departureColumn.setCellValueFactory(new PropertyValueFactory<citizen, String>("Departure"));
waste.setCellValueFactory(new PropertyValueFactory<citizen,Integer>("waste"));
remarks.setCellValueFactory(new PropertyValueFactory<citizen, String>("remarks"));
//actionColumn.setCellValueFactory(new PropertyValueFactory<>("citizenId"));;
vehicleId.setCellFactory(column -> {
return new TableCell<citizen, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(getText());
setStyle("");
} else {
if (item.equals("vehicle1")) {
setText(getText());
setStyle("-fx-background-color: yellow");
System.out.println("getting the text for vehicle 1"+getText());
} else {
setText(getText());
setStyle("-fx-background-color: tomato");
}
}
}
};
});
scheduledCitizenTable.setItems(List);}catch(NullPointerException e){};
}
}
class citizen
package gis_map;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
public class citizen {
private SimpleIntegerProperty citizenId;
private SimpleStringProperty citizenName;
private SimpleStringProperty citizenAdress;
private SimpleStringProperty arrivalTime;
private SimpleStringProperty Departure;
private SimpleStringProperty vehicleid;
private SimpleIntegerProperty waste;
private SimpleStringProperty remarks;
public citizen(String vehicleid,int citizenId, String citizenName, String citizenAdress, String arrivalTime, String Departure,int waste,String remarks) {
this.citizenId = new SimpleIntegerProperty (citizenId);
this.waste = new SimpleIntegerProperty (waste);
this.vehicleid = new SimpleStringProperty (vehicleid);
this.citizenName = new SimpleStringProperty(citizenName);
this.citizenAdress =new SimpleStringProperty (citizenAdress);
this.arrivalTime = new SimpleStringProperty(arrivalTime);
this.Departure = new SimpleStringProperty (Departure);
this.remarks = new SimpleStringProperty (remarks);
}
public int getCitizenId() {
return citizenId.get();
}
public String getVehicleid() {
return vehicleid.get();
}
public String getCitizenName() {
return citizenName.get();
}
public String getCitizenAdress() {
return citizenAdress.get();
}
public String getArrivalTime() {
return arrivalTime.get();
}
public String getDeparture() {
return Departure.get();
}
public Integer getWaste() {
return waste.get();
}
public String getRemarks() {
return remarks.get();
}
}
below is the output
click here to see results
I'm making a gui application using javafx that takes input from user and then computes that data in other class. I've tried making an object and accessing it but can't get it to work. I've googled and tried a lot of things like using abstract method but can't get it to work. please tell how can i access the variables( like ID,Party,VoterAge) from controller class or any other class
Submit.setOnAction((ActionEvent e) -> {
primaryStage.setScene(scene1);
String ID = VoterIdtext.getText();
String Party=VoteTotext.getText();
Integer VoterAge=Integer.parseInt(Agetext.getText());
}
As can be seen from your example, your variables (ID, Party, VoterAge) are described into the method and they are local variables.
When you want to use them in other classes you need to declare them in other part. For example:
public static String ID = "";
public static String Party;
public static int VoterAge = null;
...
Submit.setOnAction((ActionEvent e) -> {
primaryStage.setScene(scene1);
ID = VoterIdtext.getText();
Party=VoteTotext.getText();
VoterAge=Integer.parseInt(Agetext.getText());
}
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SampleApp extends Application {
public class Info {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = Integer.parseInt(age);
}
}
private TextField ageField;
private TextField nameField;
private Info info;
private Button printButton;
private Button submitButton;
#Override public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
primaryStage.setScene(new Scene(root));
createNameField(root);
createAgeField(root);
submitButton = new Button("Submit");
submitButton.setOnAction((e) -> setInfo());
ButtonBar e = new ButtonBar();
printButton = new Button("Print Info");
printButton.setDisable(true);
printButton.setOnAction((eve) -> printInfo());
e.getButtons().addAll(submitButton, printButton);
root.getChildren().add(e);
primaryStage.show();
}
private void printInfo() {
System.out.println("Name: " + info.getName());
System.out.println("Age: " + info.getAge());
}
private void setInfo() {
info = new Info();
info.setName(nameField.getText());
info.setAge(ageField.getText());
printButton.setDisable(false);
}
private void createAgeField(VBox root) {
HBox ageBox = new HBox(10);
root.getChildren().add(ageBox);
ageField = new TextField();
ageBox.getChildren().addAll(new Label("Age: "), ageField);
}
private void createNameField(VBox root) {
HBox ageBox = new HBox();
root.getChildren().add(ageBox);
nameField = new TextField();
ageBox.getChildren().addAll(new Label("Name: "), nameField);
}
public static void main(String[] args) {
launch(args);
}
}
I have written this little example application:
package application;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
public Person(String firstName, String lastName) {
this.firstName.set(firstName);
this.lastName.set(lastName);
}
public String getFirstName() {
return firstName.get();
}
public String getLastName() {
return lastName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty lastNameProperty() {
return lastName;
}
}
#Override
public void start(Stage primaryStage) {
try {
StackPane root = new StackPane();
TableView<Person> tv = new TableView<>();
TableColumn<Person, String> col = new TableColumn<Person, String>("FirstName");
col.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
tv.getColumns().add(col);
tv.setEditable(true);
col = new TableColumn<Person, String>("LastName");
col.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
col.setCellFactory(TextFieldTableCell.forTableColumn());
col.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> event) {
System.out.println(tv.getItems().get(1).getLastName());
}
});
tv.getColumns().add(col);
for (int i = 0; i < 30; i++) {
tv.getItems().add(new Person("Test" + i, "Test" + i));
}
root.getChildren().add(tv);
Scene scene = new Scene(root, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
tv.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
// ...
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I want to perform action when the ScrollBar has reached the bottom. Then I want to reload more datas from the database. But only then, when the user has seen all the already loaded datas (= scrollbar on the bottom). Do you have nice suggestions to solve this issue?
My first idea was to catch the MOUSE_RELEASED event (when the users drags the bar) of the TableView and then to check the position of the ScrollBar:
- getValue() gets the position of the bar
- getMax() the maximum value (=bottom).
But I can't find a way (without using the css-selector via this method) to get the ScrollBar from a given TableView. So I can't check the position of it in a certain TableView.
Do you have any ideas??
I am excited. Thanks for your help.
The only way to get the scroll bar is via a lookup, which is a bit of a hack, but it will work as long as you do it after the table has been rendered on the scene. You need
ScrollBar verticalBar = (ScrollBar) table.lookup(".scroll-bar:vertical");
Note that there's no need to mess with user events: you can just observe the scroll bar's value property directly:
verticalBar.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue.doubleValue() >= verticalBar.getMax()) {
// add more data...
}
});
SSCCE:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class AddMoreTableDataOnScrollToBottom extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Value", Item::valueProperty));
addMoreData(table, 20);
Scene scene = new Scene(new BorderPane(table), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
ScrollBar verticalBar = (ScrollBar) table.lookup(".scroll-bar:vertical");
verticalBar.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue.doubleValue() >= verticalBar.getMax()) {
addMoreData(table, 20);
}
});
}
private void addMoreData(TableView<Item> table, int numItems) {
Task<List<Item>> dataRetrieveTask = new Task<List<Item>>() {
#Override
public List<Item> call() throws Exception {
// mimic connect to db:
Thread.sleep(500);
List<Item> items = new ArrayList<>();
int nextItem = table.getItems().size() + 1 ;
for (int i = nextItem; i < nextItem + numItems; i++ ){
items.add(new Item("Item "+i, i));
}
return items ;
}
};
dataRetrieveTask.setOnSucceeded(e -> table.getItems().addAll(dataRetrieveTask.getValue()));
new Thread(dataRetrieveTask).start();
}
private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
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 IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}