JavaFX: Extracting data from a TableView - javafx

I'm currently using an application that allows the user to search through given files for data they might look for, and everything works swimmingly.
The next implementation is to allow the user to populate their own data and save it to a separate file.
Since the view-area of the searched data is identical to how I would make a form for writing new data, I figured that I would just re-use the fields.
However, I have a TableView as one of the items, and I'm not certain how you extract the information from the table.
I was presuming that since the TableView is populated with a custom Row class that I could retrieve the set of Rows that make up the table and deconstruct them that way, but I cannot ascertain how this would best be done.
Concurrently, I'm trying to learn how to override elements of the TableView's cell factories to make the cells editable (because being editable doesn't do anything by default?), so that might be a place to consolidate strategy.

You just need to call getItems() on the table and iterate through the returned list. Assuming everything is set up in the normal "JavaFX way", any properties in the lists elements will be automatically updated by the editing mechanism.
Here is a complete, FXML-based example.
EditableTableExample.fxml (in package application):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.TextFieldTableCell?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1"
fx:controller="application.EditableTableViewController">
<center>
<TableView fx:id="table" editable="true">
<columns>
<TableColumn text="First Name" prefWidth="150"
fx:id="firstNameColumn">
<cellFactory>
<TextFieldTableCell fx:factory="forTableColumn" />
</cellFactory>
</TableColumn>
<TableColumn text="Last Name" prefWidth="150" fx:id="lastNameColumn">
<cellFactory>
<TextFieldTableCell fx:factory="forTableColumn" />
</cellFactory>
</TableColumn>
<TableColumn text="Email" prefWidth="150" fx:id="emailColumn">
<cellFactory>
<TextFieldTableCell fx:factory="forTableColumn" />
</cellFactory>
</TableColumn>
</columns>
</TableView>
</center>
<bottom>
<HBox alignment="CENTER">
<padding>
<Insets top="10" right="10" left="10" bottom="10" />
</padding>
<children>
<Button onAction="#showData" text="Show Data" />
</children>
</HBox>
</bottom>
</BorderPane>
EditableTableController:
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
public class EditableTableViewController {
#FXML
private TableView<Person> table ;
#FXML
private TableColumn<Person, String> firstNameColumn ;
#FXML
private TableColumn<Person, String> lastNameColumn ;
#FXML
private TableColumn<Person, String> emailColumn ;
public void initialize() {
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
emailColumn.setCellValueFactory(cellData -> cellData.getValue().emailProperty());
table.getItems().addAll(
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")
) ;
}
#FXML
private void showData() {
for (Person person : table.getItems()) {
String formatted = String.format("%s %s (%s)", person.getFirstName(), person.getLastName(), person.getEmail());
System.out.println(formatted);
}
System.out.println();
}
}
Model class Person:
package application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
public final String getFirstName() {
return firstNameProperty().get();
}
public final void setFirstName(String firstName) {
firstNameProperty().set(firstName);
}
public StringProperty firstNameProperty() {
return firstName ;
}
private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
public final String getLastName() {
return lastNameProperty().get();
}
public final void setLastName(String lastName) {
lastNameProperty().set(lastName);
}
public StringProperty lastNameProperty() {
return lastName ;
}
private final StringProperty email = new SimpleStringProperty(this, "email");
public final String getEmail() {
return emailProperty().get();
}
public final void setEmail(String email) {
emailProperty().set(email);
}
public StringProperty emailProperty() {
return email ;
}
public Person(String firstName, String lastName, String email) {
this.setFirstName(firstName);
this.setLastName(lastName);
this.setEmail(email);
}
}
Application class:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class EditableTableViewExample extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("EditableTableExample.fxml"));
BorderPane root = loader.load();
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The button is just for demonstration, but pressing it will invoke the showData() method in the controller, which iterates through the table's list of items and retrieves the property values from each one. If you double-click a cell to edit, type to change the value, and then commit the edit with Enter, then pressing the button will show the updated values.

Related

JavaFx Tableview how to add a listener to checkbox column

I'm just starting to work with JavaFX. I create a TableView with 3 columns, (name, last name and select). This last is a Checkbox column. this is my code:
PersonTableController.java:
package ch.makery.sortfilter;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
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;
/**
* View-Controller for the person table.
*
* #author Marco Jakob
*/
public class PersonTableController {
#FXML
private TextField filterField;
#FXML
private TableView<Person> personTable;
#FXML
private TableColumn<Person, String> firstNameColumn;
#FXML
private TableColumn<Person, String> lastNameColumn;
#FXML
private TableColumn<Person, String> selectColumn;
private ObservableList<Person> masterData = FXCollections.observableArrayList();
/**
* Just add some sample data in the constructor.
*/
public PersonTableController() {
masterData.add(new Person("Hans", "Muster"));
masterData.add(new Person("Ruth", "Mueller"));
masterData.add(new Person("Heinz", "Kurz"));
masterData.add(new Person("Cornelia", "Meier"));
masterData.add(new Person("Werner", "Meyer"));
masterData.add(new Person("Lydia", "Kunz"));
masterData.add(new Person("Anna", "Best"));
masterData.add(new Person("Stefan", "Meier"));
masterData.add(new Person("Martin", "Mueller"));
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*
* Initializes the table columns and sets up sorting and filtering.
*/
#FXML
private void initialize() {
// 0. Initialize the columns.
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
selectColumn.setCellValueFactory(
new PropertyValueFactory<Person,String>("select")
);
// 1. Wrap the ObservableList in a FilteredList (initially display all data).
FilteredList<Person> filteredData = new FilteredList<>(masterData, p -> true);
// 2. Set the filter Predicate whenever the filter changes.
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(person -> {
// If filter text is empty, display all persons.
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Compare first name and last name of every person with filter text.
String lowerCaseFilter = newValue.toLowerCase();
if (person.getFirstName().toLowerCase().indexOf(lowerCaseFilter) != -1) {
return true; // Filter matches first name.
} else if (person.getLastName().toLowerCase().indexOf(lowerCaseFilter) != -1) {
return true; // Filter matches last name.
}
return false; // Does not match.
});
});
// 3. Wrap the FilteredList in a SortedList.
SortedList<Person> sortedData = new SortedList<>(filteredData);
// 4. Bind the SortedList comparator to the TableView comparator.
// Otherwise, sorting the TableView would have no effect.
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
// 5. Add sorted (and filtered) data to the table.
personTable.setItems(sortedData);
}
}
Person.java:
package ch.makery.sortfilter;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.CheckBox;
/**
* Simple model class for the person table.
*
* #author Marco Jakob
*/
public class Person {
private final StringProperty firstName;
private final StringProperty lastName;
private CheckBox selec = new CheckBox();
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public CheckBox getSelect() {
return select;
}
public void setSelect(CheckBox select) {
this.select = select;
}
}
PersonTable.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane minWidth="315.0" prefHeight="300.0" prefWidth="315.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.makery.sortfilter.PersonTableController">
<children>
<TableView fx:id="personTable" prefHeight="-1.0" prefWidth="-1.0" tableMenuButtonVisible="false" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="40.0">
<columns>
<TableColumn fx:id="firstNameColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="First Name" />
<TableColumn fx:id="lastNameColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="Last Name" />
<TableColumn fx:id="selectColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="Select" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<HBox id="HBox" alignment="CENTER" spacing="5.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<children>
<Label text="Filter Table:" />
<TextField fx:id="filterField" prefWidth="-1.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>
</children>
</AnchorPane>
I want than just one person can be selected at the time, so try unsuccessfully to add a event or listener to do that. If any checkbox is checked and user check one of them, is OK. But if one checkbox is checked and other checkbox is checked at same time, then the first one must to be unchecked, just like RadioButton works.
Some one can help me?
Best regards
Thank you #James_D.
finally I can implement the "just-one" checkbox selection. I followed the example than you show me:
javafx-checkboxtablecell-get-actionevent-when-user-check-a-checkbox
And I add this code to call event:
checkedCol.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer,
ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer param) {
System.out.println("Cours "+coursData.get(param).getCours()+" changed value to " +coursData.get(param).isChecked());
for (Integer i = 0; i < coursData.size(); i++) {
if (i != param && coursData.get(param).isChecked()) {
coursData.get(i).setChecked(false);
} // if
} // for
return coursData.get(param).checkedProperty();
}
}));
in for cicle, I uncheck the rest of checkboxes in table.
Thank you very much for help.
Best Regards.
Leo.

Can't add data to TableView if controller is specified in FXML. Can't bind `onAction` if controller is NOT specified

I am learning JavaFX FXML. In order to add data to TableView I add controller to FXMLLoader in a POJO class.
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/my_view.fxml"));
fxmlLoader.setController(controller);
try {
VBox myView = fxmlLoader.load();
controller.getInboxTable().setItems(getMyDisplayedItems());
//...
Root of FXML file has this definition:
<VBox fx:id="rootVBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
Thus, I can't specify controller in FXML file.
When I define Buttons in the same class I can't specify onMouseClicked because "No controller specified for top level element".
So, I can either populate TableView with data or attach action handler, but not both. What is the correct way to attach SortedList to TableView AND specify onAction in FXML?
If you want to use FXML, try to manage all the View layer's code there. You can add the controller in the FXML file via the fx:controller tag. Have a look at the Creating an Address Book with FXML at Oracle Docs. Most of the code below is from that tutorial.
Another thing is that using such a way of assigning the controller should fix the "No controller specified for top level element" issue. I added a button which causes the Table View's data is shuffled.
So, assuming you have all the files in the folder called sample in your project folder:
Main.java:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
#Override
public void start(final Stage stage) {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(Main.class.getResource("/sample/sample.fxml"));
try {
Parent root;
root = fxmlLoader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
sample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<VBox fx:id="rootVBox"
xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.Controller">
<Button text="Shuffle Data" onAction="#shuffleDataButtonClicked"/>
<TableView fx:id="tableView">
<columns>
<TableColumn text="First Name">
<cellValueFactory><PropertyValueFactory property="firstName" />
</cellValueFactory>
</TableColumn>
<TableColumn text="Last Name">
<cellValueFactory><PropertyValueFactory property="lastName" />
</cellValueFactory>
</TableColumn>
<TableColumn text="Email Address">
<cellValueFactory><PropertyValueFactory property="email" />
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</VBox>
Controller.java:
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import java.util.Collections;
import java.util.Vector;
public class Controller {
#FXML
private TableView<Person> tableView;
#FXML
private void initialize() {
tableView.getItems().addAll(getSomePersonData());
}
private Vector<Person> getSomePersonData() {
Person jacobSmith = new Person("Jacob", "Smith", "jacob.smith#example.com");
Person isabellaJohnson = new Person("Isabella", "Johnson", "isabella.johnson#example.com");
Person ethanWilliams = new Person("Ethan", "Williams", "ethan.williams#example.com");
Person emmaJones = new Person("Emma", "Jones", "emma.jones#example.com");
Person michaelBrown = new Person("Michael", "Brown", "michael.brown#example.com");
Vector<Person> people = new Vector<>();
people.add(jacobSmith);
people.add(isabellaJohnson);
people.add(ethanWilliams);
people.add(emmaJones);
people.add(michaelBrown);
return people;
}
#FXML
private void shuffleDataButtonClicked() {
Collections.shuffle(tableView.getItems());
}
}
Person.java
package sample;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleStringProperty firstName = new SimpleStringProperty("");
private final SimpleStringProperty lastName = new SimpleStringProperty("");
private final SimpleStringProperty email = new SimpleStringProperty("");
public Person() {
this("", "", "");
}
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(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);
}
}

javafx-TableView as combobox popup (Tried and able to achieve partially. need help further)

What i need:
Need an editable combobox which filters the data on the popup upon typing and first matching item should be highlighted and should set as the text in the combo upon pressing enter.
The popup should be a tableview with 2 or 3 columns. (screen shot attached.)(in the image it is a textfield but i prefer combo so that if the user is not sure about the values, he can click the combo button and see the entire list and select one.)
I will be binding some data as a source to the tableview which should act as the popup for the combobox.
I know i should go for a custom control but dont know where to start?
Reference URL: enter link description here
Thanks in Advance.
What i have tried so far:
Guys,
With the idea you guys gave, I have tried this so far and able to achieve.
(For now, i am not showing the tableview dynamically below the text field (eventually dats wat i want))
1. A tableview with static data is already loaded and added to the scene.
2. Having a text field below the tableview. (this is named as txt)
3. Having another text field below the first text field(this is named as txt1) (when i press tab from the previous text field cursor should come here)
i have a predicate set for the table and i am updating my predicate as the user types in the txt. (working)
Able to filter the table and the first matching row will highlighted. (working)
When the user press either "Tab" or "Enter" the highlighted row in the tableview should be set as the value in the txt.(working)
Needed:
1. When i press "Tab" or "Enter", the highlighted row will be set as the value in the text field (txt) and also the cursor should move to next focusable node. in my case it is the 2nd text field (txt1). I dont want to say txt1.requestFocus() bcoz in realtime, i have many text fields in the scene and this control will be a user control. so cant hardcode anything.
When the user types some text in the text field(not full text. eg: "do" where my table contains "Dom", "Don"), if there are multiple matches, currently both the records will be displayed in the table but the first one will be highlighted. User should be able to select the 2nd row if he wants by pressing the down arrow from the text field itself.
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
VBox root = FXMLLoader.load(this.getClass().getResource("MainView.fxml"));
Scene scene = new Scene(root,500,300);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
MainController.java
package application;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Popup;
public class MainController implements Initializable
{
private #FXML TableView<Person> table;
private #FXML TableColumn<Person, String> firstNameCol;
private #FXML TableColumn<Person, String> lastNameCol;
private #FXML TableColumn<Person, String> emailCol;
private #FXML TableColumn<Person, Integer> ageCol;
private #FXML TextField txt;
private #FXML TextField txt1;
private #FXML Button btn;
#Override
public void initialize(URL location, ResourceBundle resources)
{
Platform.runLater(new Runnable() {
#Override
public void run() {
txt.requestFocus();
}
});
ObservableList<Person> obsList =FXCollections.observableArrayList();
obsList.add(new Person("Sam", "P1LasttName", "P1Email#gmail.com", 20));
obsList.add(new Person("Dom", "P2LasttName", "P2Email#gmail.com", 30));
obsList.add(new Person("Ken", "P3LasttName", "P3Email#gmail.com", 40));
obsList.add(new Person("Don", "P4LasttName", "P4Email#gmail.com", 50));
obsList.add(new Person("Tom", "P5LasttName", "P5Email#gmail.com", 60));
FilteredList<Person> filteredList = new FilteredList<>(obsList, p->true);
table.setItems(filteredList);
txt.textProperty().addListener((obs, oldValue, newValue) ->{
filteredList.setPredicate(person-> {
if(newValue == null || newValue.isEmpty())
return true;
if(person.getFirstName().trim().toLowerCase().contains(newValue.toLowerCase()))
return true;
return false;
});
Platform.runLater(new Runnable() {
#Override
public void run()
{
// we don't want repeated selections
table.getSelectionModel().clearSelection();
//get the focus
table.requestFocus();
//select first item in TableView model
table.getSelectionModel().selectFirst();
//set the focus on the first element
table.getFocusModel().focus(0);
//render the selected item in the TableView
//tableClickHandler(null);
}
});
Platform.runLater(new Runnable() {
#Override
public void run()
{
txt.requestFocus();
txt.end();
}
});
});
table.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event)
{
if(event.getCode() == KeyCode.ENTER)
{
txt.setText(table.getSelectionModel().getSelectedItem().getFirstName());
}
}
});
txt.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event)
{
if(event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB)
//if(event.getCode() == KeyCode.ENTER)
{
txt.setText(table.getSelectionModel().getSelectedItem().getFirstName());
/*Platform.runLater(new Runnable() {
public void run() {
txt1.requestFocus();
}
});*/
}
}
});
/*txt.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event)
{
if(event.getCode() == KeyCode.TAB)
{
//txt.setText(table.getSelectionModel().getSelectedItem().getFirstName());
if(txt.getSkin() instanceof BehaviorSkinBase)
{
//((BehaviorSkinBase)txt.getSkin()).getBehavior().traverseNext();
BehaviorBase x = ((BehaviorSkinBase)txt.getSkin()).getBehavior();
((TextFieldBehavior)x).callAction("TraverseNext");
}
event.consume();
}
}
});*/
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
/*
Popup popup = new Popup();
popup.getContent().add(new TableView());
//popup.show(txt, txt.localToScreen(0, 0).getX() + txt.getWidth()/2, txt.localToScreen(0, 0).getY() + txt.getHeight());
popup.show(txt, txt.localToScreen(0, 0).getX(), txt.localToScreen(0, 0).getY() + txt.getHeight() + 2);
*/
Parent vbox = null;
try {
vbox = FXMLLoader.load(this.getClass().getResource("TableView.fxml"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Popup popup = new Popup();
popup.getContent().add(vbox);
//popup.show(txt, txt.localToScreen(0, 0).getX() + txt.getWidth()/2, txt.localToScreen(0, 0).getY() + txt.getHeight());
//popup.show(txt, txt.localToScreen(0, 0).getX(), txt.localToScreen(0, 0).getY() + txt.getHeight() + 2);
popup.show(txt, txt.localToScreen(0, 0).getX(), txt.localToScreen(0, 0).getY() + txt.getHeight() + 2);
}
});
}
}
Person.java
package application;
public class Person
{
private String firstName;
private String lastName;
private String email;
private Integer age;
public Person(){}
public Person(String firstName, String lastName, String email, Integer age)
{
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.age = age;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public Integer getAge()
{
return age;
}
public void setAge(Integer age)
{
this.age = age;
}
}
application.css
.table-row-cell:selected
{
-fx-background-color: lightgreen;
/* the below style will remove the border lines of the selected row */
-fx-table-cell-border-color: transparent;
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?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.VBox?>
<!-- <?import application.Person?> -->
<VBox spacing="10.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<TableView fx:id="table" prefHeight="250.0" prefWidth="437.0">
<columns>
<TableColumn fx:id="firstNameCol" prefWidth="120.0" text="First Name">
<cellValueFactory><PropertyValueFactory property="firstName" /></cellValueFactory>
</TableColumn>
<TableColumn fx:id="lastNameCol" prefWidth="120.0" text="Last Name">
<cellValueFactory><PropertyValueFactory property="lastName" /></cellValueFactory>
</TableColumn>
<TableColumn fx:id="emailCol" prefWidth="120.0" text="Email">
<cellValueFactory><PropertyValueFactory property="email" /></cellValueFactory>
</TableColumn>
<TableColumn fx:id="ageCol" prefWidth="75.0" text="Age">
<cellValueFactory><PropertyValueFactory property="age" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
<Button text="Button" fx:id="btn"/>
<TextField fx:id="txt" promptText="Type to Filter" />
<TextField fx:id="txt1" promptText="Focus should be here when tab is pressed from pervious txt" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
TableView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.layout.VBox?>
<?import application.Person?>
<?import javafx.collections.*?>
<!--
<VBox xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65">
<children>
-->
<!-- <TableView xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:id="table" prefHeight="160.0" prefWidth="440.0"> -->
<TableView xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:id="table" prefHeight="140.0">
<columns>
<TableColumn fx:id="firstNameCol" prefWidth="120.0" text="First Name">
<cellValueFactory><PropertyValueFactory property="firstName" /></cellValueFactory>
</TableColumn>
<TableColumn fx:id="lastNameCol" prefWidth="120.0" text="Last Name">
<cellValueFactory><PropertyValueFactory property="lastName" /></cellValueFactory>
</TableColumn>
<TableColumn fx:id="emailCol" prefWidth="120.0" text="Email">
<cellValueFactory><PropertyValueFactory property="email" /></cellValueFactory>
</TableColumn>
<TableColumn fx:id="ageCol" prefWidth="75.0" text="Age">
<cellValueFactory><PropertyValueFactory property="age" /></cellValueFactory>
</TableColumn>
</columns>
<columnResizePolicy><TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /></columnResizePolicy>
<items>
<FXCollections fx:factory="observableArrayList">
<Person firstName="P1FirstName" lastName="P1LasttName" email="P1Email#gmail.com" age="20"/>
<Person firstName="P2FirstName" lastName="P2LasttName" email="P2Email#gmail.com" age="30"/>
<Person firstName="P3FirstName" lastName="P3LasttName" email="P3Email#gmail.com" age="40"/>
<Person firstName="P4FirstName" lastName="P4LasttName" email="P4Email#gmail.com" age="50"/>
<Person firstName="P5FirstName" lastName="P5LasttName" email="P5Email#gmail.com" age="60"/>
</FXCollections>
</items>
</TableView>
<!--
</children>
</VBox>
-->
Any help is appreciated. Thanks!

Can't populate JavaFX TableView created in SceneBuilder 2

I am trying to re-create the table view sample using scene builder 2, but my TableView couldn’t be populated.
I defined my table in JavaFx SceneBuilder like so:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="380.0" prefWidth="462.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="tableviewsample.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="325.0" layoutY="324.0" onAction="#handleButtonAction" text="Click Me!" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<TableView fx:id="table" layoutX="26.0" layoutY="29.0" prefHeight="221.0" prefWidth="411.0">
<columns>
<TableColumn fx:id="firstNameCol" prefWidth="75.0" text="First Name" />
<TableColumn fx:id="lastNameCol" prefWidth="75.0" text="Last Name" />
<TableColumn fx:id="emailCol" prefWidth="75.0" text="Email" />
</columns>
</TableView>
<TextField fx:id="addFirstName" layoutX="26.0" layoutY="284.0" prefHeight="25.0" prefWidth="81.0" promptText="First Name" />
<TextField fx:id="addLastName" layoutX="121.0" layoutY="284.0" prefHeight="25.0" prefWidth="89.0" promptText="Last Name" />
<TextField fx:id="addEmail" layoutX="222.0" layoutY="284.0" promptText="Email" />
</children>
</AnchorPane>
My controller code:
package tableviewsample;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private TableView<Person> table;// = new TableView<Person>();
#FXML private TableColumn firstNameCol ;
#FXML private TableColumn lastNameCol ;
#FXML private TableColumn emailCol ;
#FXML TextField addFirstName;
#FXML TextField addLastName;
#FXML TextField addEmail;
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")
);
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
firstNameCol.setMinWidth(100);
lastNameCol.setMinWidth(100);
emailCol.setMinWidth(200);
table.getItems().setAll(this.data);
}
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);
}
}
}
The main code:
package tableviewsample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TableViewSample extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Screenshot:
When I run it, there are empty rows in the table. If I type some values in the 3 TextFields and click the button, still the values are not inserted in the table.
Can you please find what's wrong with the code? Also, on an unrelated note, how can I make the table scrollable?
You haven't set any cellValueFactorys on your TableColumns (Example 12-5 in the tutorial you linked).
You can do this in FXML with
<TableColumn fx:id="firstNameCol" prefWidth="75.0" text="First Name" >
<cellValueFactory><PropertyValueFactory property="firstName"/></cellValueFactory>
</TableColumn>
or in the controller with
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
(and similarly for the other columns)
You've actually made two mistakes.
First of all, you're trying to assign the datamodel to the TableView using getItems().setAll(). Take a look at the documentation of setAll(). It copies all the values of the given list into the ObservableList. This is not what you want, you want the TableView to start observing data and responding to changes. The way to do this is to assign a reference directly, through setItems(data).
Take a look at the code examples in the documentation of TableView. You forgot to connect the various StringProperties in Person up to the columns. This is the way they do it:
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
Lastly, I've got a little sidenote. Note that this is NOT the cause of your specific issue.
I see you're using raw types. This is generally considered a bad thing, and you're probably getting some warnings from the compiler about it. You can solve the issue by stating the generic types your objects use. Notice in the above code sample, I state for the PropertyValueFactories that the type of class contained within the TableView.items is Person and that the type of object displayed in the rows is String.
As for scrolling, which you also asked for, it does that automatically once there are enough lines in the TableView.
IN handle Mouse Button you can write:
/setItems();
follow the code of green tick above

Is it possible combine fx:include and afterburner

I currently using afterburner.fx to tailor together components of JavaFX based application.
Right now I trying move components in separate fxml files for more comfortable maintenance.
To load such components I using fx:include directive which allow load nested components automatically.
Problem is that with automatic load I loosing possibility get presenter from nested view.
Is there a way to combine automatic load and in same time, be able work with nested components from parent root?
These two seem to work fine together.
Afterburner works by setting a controller factory on the FXML loader, which takes care of instantiating the presenter class and injecting values into it.
The <fx:include> element will propagate the controller factory when loading the included FXML, so you can also inject values into the controller defined in the included FXML. Because afterburner effectively uses a singleton scope for injection, the same instance of injected fields will be used. This means you can readily share your data model between the different presenter classes.
If you want access to the presenter associated with the included FXML, just use the standard technique for "nested controllers".
So, for example:
main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="application.MainPresenter">
<center>
<TableView fx:id="table">
<columns>
<TableColumn text="First Name" prefWidth="150">
<cellValueFactory>
<PropertyValueFactory property="firstName" />
</cellValueFactory>
</TableColumn>
<TableColumn text="Last Name" prefWidth="150">
<cellValueFactory>
<PropertyValueFactory property="lastName" />
</cellValueFactory>
</TableColumn>
<TableColumn text="Email" prefWidth="200">
<cellValueFactory>
<PropertyValueFactory property="email" />
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</center>
<bottom>
<fx:include source="Editor.fxml" fx:id="editor">
<padding>
<Insets top="5" bottom="5" left="5" right="5"/>
</padding>
</fx:include>
</bottom>
</BorderPane>
editor.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<GridPane xmlns:fx="http://javafx.com/fxml" hgap="5" vgap="10" fx:controller="application.EditorPresenter">
<Label GridPane.rowIndex="0" GridPane.columnIndex="0" text="First Name:"/>
<TextField fx:id="firstNameTextField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Label GridPane.rowIndex="1" GridPane.columnIndex="0" text="Last Name"/>
<TextField fx:id="lastNameTextField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label GridPane.rowIndex="2" GridPane.columnIndex="0" text="Email"/>
<TextField fx:id="emailTextField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<HBox GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2">
<Button fx:id="addEditButton" onAction="#addEdit" />
</HBox>
</GridPane>
MainPresenter.java:
package application;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
public class MainPresenter {
#FXML
private TableView<Person> table ;
// This is the controller (presenter) for the included fxml
// It is injected by the FXMLLoader; the rule is that "Controller" needs to be
// appended to the fx:id attribute of the <fx:include> tag.
// This is not used in this example but is here to demonstrate how to access it
// if needed.
#FXML
private EditorPresenter editorController ;
#Inject
private DataModel dataModel ;
public void initialize() {
table.setItems(dataModel.getPeople());
table.getSelectionModel().selectedItemProperty().addListener(
(obs, oldPerson, newPerson) -> dataModel.setCurrentPerson(newPerson));
dataModel.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson == null) {
table.getSelectionModel().clearSelection();
} else {
table.getSelectionModel().select(newPerson);
}
});
dataModel.getPeople().addAll(
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")
);
}
}
EditorPresenter.java:
package application;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javax.inject.Inject;
public class EditorPresenter {
#FXML
private TextField firstNameTextField ;
#FXML
private TextField lastNameTextField ;
#FXML
private TextField emailTextField ;
#FXML
private Button addEditButton ;
#Inject
private DataModel dataModel ;
public void initialize() {
addEditButton.textProperty().bind(
Bindings.when(Bindings.isNull(dataModel.currentPersonProperty()))
.then("Add")
.otherwise("Update")
);
dataModel.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson == null) {
firstNameTextField.setText("");
lastNameTextField.setText("");
emailTextField.setText("");
} else {
firstNameTextField.setText(newPerson.getFirstName());
lastNameTextField.setText(newPerson.getLastName());
emailTextField.setText(newPerson.getEmail());
}
});
}
#FXML
private void addEdit() {
Person person = dataModel.getCurrentPerson();
String firstName = firstNameTextField.getText();
String lastName = lastNameTextField.getText();
String email = emailTextField.getText();
if (person == null) {
dataModel.getPeople().add(new Person(firstName, lastName, email));
} else {
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail(email);
}
}
}
MainView.java:
package application;
import com.airhacks.afterburner.views.FXMLView;
public class MainView extends FXMLView {
}
Main.java (application class):
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import com.airhacks.afterburner.injection.Injector;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
MainView mainView = new MainView();
Scene scene = new Scene(mainView.getView(), 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() throws Exception {
Injector.forgetAll();
}
public static void main(String[] args) {
launch(args);
}
}
DataModel.java:
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class DataModel {
private final ObservableList<Person> people = FXCollections.observableArrayList();
private final ObjectProperty<Person> currentPerson = new SimpleObjectProperty<>(this, "currentPerson");
public ObservableList<Person> getPeople() {
return people ;
}
public final Person getCurrentPerson() {
return currentPerson.get();
}
public final void setCurrentPerson(Person person) {
this.currentPerson.set(person);
}
public ObjectProperty<Person> currentPersonProperty() {
return currentPerson ;
}
}
And the usual Person.java example:
package application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
public final String getFirstName() {
return firstName.get();
}
public final void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName ;
}
private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
public final String getLastName() {
return lastName.get();
}
public final void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName ;
}
private final StringProperty email = new SimpleStringProperty(this, "email");
public final String getEmail() {
return email.get();
}
public final void setEmail(String email) {
this.email.set(email);
}
public StringProperty emailProperty() {
return email ;
}
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
}

Resources