└── JavaFXTableTutorial
├── .classpath
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── bin
└── lankydan
│ └── tutorials
│ └── fxml
│ ├── TableApp.fxml
│ ├── cell
│ ├── EditCell$1.class
│ ├── EditCell$2.class
│ └── EditCell.class
│ ├── controller
│ ├── PersonTableData.class
│ └── TableAppController.class
│ ├── converter
│ ├── MyDateStringConverter.class
│ └── MyDoubleStringConverter.class
│ ├── css.css
│ ├── data
│ └── Person.class
│ └── launcher
│ └── TableAppLauncher.class
├── resources
└── lankydan
│ └── tutorials
│ └── fxml
│ ├── TableApp.fxml
│ └── css.css
└── src
└── lankydan
└── tutorials
└── fxml
├── cell
└── EditCell.java
├── controller
├── PersonTableData.java
└── TableAppController.java
├── converter
├── MyDateStringConverter.java
└── MyDoubleStringConverter.java
├── data
└── Person.java
└── launcher
└── TableAppLauncher.java
/JavaFXTableTutorial/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | JavaFXTableTutorial
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.8
12 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/TableApp.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/cell/EditCell$1.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/cell/EditCell$1.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/cell/EditCell$2.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/cell/EditCell$2.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/cell/EditCell.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/cell/EditCell.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/controller/PersonTableData.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/controller/PersonTableData.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/controller/TableAppController.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/controller/TableAppController.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/converter/MyDateStringConverter.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/converter/MyDateStringConverter.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/converter/MyDoubleStringConverter.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/converter/MyDoubleStringConverter.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/css.css:
--------------------------------------------------------------------------------
1 | #pane, .root, .split-pane{
2 | -fx-background-color: #353434;
3 | -fx-foreground-color: #353434;
4 | }
5 |
6 | .label {
7 | -fx-font-size: 11pt;
8 | -fx-font-family: "Segoe UI Semibold";
9 | -fx-text-fill: white;
10 | -fx-opacity: 0.6;
11 | -fx-background-color: #353434;
12 | }
13 |
14 | .label-bright {
15 | -fx-font-size: 11pt;
16 | -fx-font-family: "Segoe UI Semibold";
17 | -fx-text-fill: white;
18 | -fx-opacity: 1;
19 | }
20 |
21 | .label-header {
22 | -fx-font-size: 32pt;
23 | -fx-font-family: "Segoe UI Light";
24 | -fx-text-fill: white;
25 | -fx-opacity: 1;
26 | }
27 |
28 | .table-view {
29 | -fx-base: #353434;
30 | -fx-control-inner-background: #353434;
31 | -fx-background-color: #353434;
32 | -fx-table-cell-border-color: transparent;
33 | -fx-table-header-border-color: transparent;
34 | -fx-padding: 5;
35 | }
36 |
37 | .table-view .column-header .label {
38 | -fx-font-size: 12pt;
39 | -fx-font-family: "Segoe UI Light";
40 | -fx-text-fill: white;
41 | -fx-alignment: center-left;
42 | -fx-opacity: 1;
43 | }
44 |
45 | .table-view:focused .table-row-cell:filled:focused:selected {
46 | -fx-background-color: -fx-focus-color;
47 | }
48 |
49 | .background {
50 | -fx-background-color: #674A44;
51 | -fx-foreground-color: #353434;
52 | }
53 |
54 | .button {
55 | -fx-padding: 5 22 5 22;
56 | -fx-border-color: #353434;
57 | -fx-border-width: 0;
58 | -fx-background-radius: 0;
59 | -fx-background-color: derive(#353434,20%);
60 | -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
61 | -fx-font-size: 11pt;
62 | -fx-text-fill: #d8d8d8;
63 | -fx-background-insets: 0 0 0 0, 0, 1, 2;
64 | }
65 |
66 | .button:hover {
67 | -fx-background-color: #3a3a3a;
68 | }
69 |
70 | .button:pressed, .button:default:hover:pressed {
71 | -fx-background-color: #bdbcbc;
72 | -fx-text-fill: black;
73 | }
74 |
75 | .button:disabled, .button:default:disabled {
76 | -fx-opacity: 0.4;
77 | -fx-background-color: #353434;
78 | -fx-text-fill: white;
79 | }
80 |
81 | .button:default {
82 | -fx-background-color: -fx-focus-color;
83 | -fx-text-fill: #ffffff;
84 | }
85 |
86 | .button:default:hover {
87 | -fx-background-color: derive(-fx-focus-color,30%);
88 | }
89 |
90 | .text-area .content {
91 | -fx-background-color: #575758;
92 | }
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/data/Person.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/data/Person.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/launcher/TableAppLauncher.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lankydan/JavaFX-Table-Tutorial/75207595405a411a8f1a8370e867a96eecab2991/JavaFXTableTutorial/bin/lankydan/tutorials/fxml/launcher/TableAppLauncher.class
--------------------------------------------------------------------------------
/JavaFXTableTutorial/resources/lankydan/tutorials/fxml/TableApp.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/resources/lankydan/tutorials/fxml/css.css:
--------------------------------------------------------------------------------
1 | #pane, .root, .split-pane{
2 | -fx-background-color: #353434;
3 | -fx-foreground-color: #353434;
4 | }
5 |
6 | .label {
7 | -fx-font-size: 11pt;
8 | -fx-font-family: "Segoe UI Semibold";
9 | -fx-text-fill: white;
10 | -fx-opacity: 0.6;
11 | -fx-background-color: #353434;
12 | }
13 |
14 | .label-bright {
15 | -fx-font-size: 11pt;
16 | -fx-font-family: "Segoe UI Semibold";
17 | -fx-text-fill: white;
18 | -fx-opacity: 1;
19 | }
20 |
21 | .label-header {
22 | -fx-font-size: 32pt;
23 | -fx-font-family: "Segoe UI Light";
24 | -fx-text-fill: white;
25 | -fx-opacity: 1;
26 | }
27 |
28 | .table-view {
29 | -fx-base: #353434;
30 | -fx-control-inner-background: #353434;
31 | -fx-background-color: #353434;
32 | -fx-table-cell-border-color: transparent;
33 | -fx-table-header-border-color: transparent;
34 | -fx-padding: 5;
35 | }
36 |
37 | .table-view .column-header .label {
38 | -fx-font-size: 12pt;
39 | -fx-font-family: "Segoe UI Light";
40 | -fx-text-fill: white;
41 | -fx-alignment: center-left;
42 | -fx-opacity: 1;
43 | }
44 |
45 | .table-view:focused .table-row-cell:filled:focused:selected {
46 | -fx-background-color: -fx-focus-color;
47 | }
48 |
49 | .background {
50 | -fx-background-color: #674A44;
51 | -fx-foreground-color: #353434;
52 | }
53 |
54 | .button {
55 | -fx-padding: 5 22 5 22;
56 | -fx-border-color: #353434;
57 | -fx-border-width: 0;
58 | -fx-background-radius: 0;
59 | -fx-background-color: derive(#353434,20%);
60 | -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
61 | -fx-font-size: 11pt;
62 | -fx-text-fill: #d8d8d8;
63 | -fx-background-insets: 0 0 0 0, 0, 1, 2;
64 | }
65 |
66 | .button:hover {
67 | -fx-background-color: #3a3a3a;
68 | }
69 |
70 | .button:pressed, .button:default:hover:pressed {
71 | -fx-background-color: #bdbcbc;
72 | -fx-text-fill: black;
73 | }
74 |
75 | .button:disabled, .button:default:disabled {
76 | -fx-opacity: 0.4;
77 | -fx-background-color: #353434;
78 | -fx-text-fill: white;
79 | }
80 |
81 | .button:default {
82 | -fx-background-color: -fx-focus-color;
83 | -fx-text-fill: #ffffff;
84 | }
85 |
86 | .button:default:hover {
87 | -fx-background-color: derive(-fx-focus-color,30%);
88 | }
89 |
90 | .text-area .content {
91 | -fx-background-color: #575758;
92 | }
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/cell/EditCell.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.cell;
2 |
3 | import javafx.beans.value.ChangeListener;
4 | import javafx.beans.value.ObservableValue;
5 | import javafx.event.ActionEvent;
6 | import javafx.event.Event;
7 | import javafx.event.EventHandler;
8 | import javafx.scene.control.TableCell;
9 | import javafx.scene.control.TableColumn;
10 | import javafx.scene.control.TableColumn.CellEditEvent;
11 | import javafx.scene.control.TablePosition;
12 | import javafx.scene.control.TableView;
13 | import javafx.scene.control.TextField;
14 | import javafx.scene.control.cell.TextFieldTableCell;
15 | import javafx.scene.input.KeyCode;
16 | import javafx.scene.input.KeyEvent;
17 | import javafx.util.Callback;
18 | import javafx.util.StringConverter;
19 | import javafx.util.converter.DefaultStringConverter;
20 |
21 | public class EditCell extends TextFieldTableCell {
22 |
23 | private TextField textField;
24 | private boolean escapePressed = false;
25 | private TablePosition tablePos = null;
26 |
27 | public EditCell(final StringConverter converter) {
28 | super(converter);
29 | }
30 |
31 | public static Callback, TableCell> forTableColumn() {
32 | return forTableColumn(new DefaultStringConverter());
33 | }
34 |
35 | public static Callback, TableCell> forTableColumn(
36 | final StringConverter converter) {
37 | return list -> new EditCell(converter);
38 | }
39 |
40 | @Override
41 | public void startEdit() {
42 | if (!isEditable() || !getTableView().isEditable()
43 | || !getTableColumn().isEditable()) {
44 | return;
45 | }
46 | super.startEdit();
47 |
48 | if (isEditing()) {
49 | if (textField == null) {
50 | textField = getTextField();
51 | }
52 | escapePressed = false;
53 | startEdit(textField);
54 | final TableView table = getTableView();
55 | tablePos = table.getEditingCell();
56 | }
57 | }
58 |
59 | /** {@inheritDoc} */
60 | @Override
61 | public void commitEdit(T newValue) {
62 | if (!isEditing())
63 | return;
64 | final TableView table = getTableView();
65 | if (table != null) {
66 | // Inform the TableView of the edit being ready to be committed.
67 | CellEditEvent editEvent = new CellEditEvent(table, tablePos,
68 | TableColumn.editCommitEvent(), newValue);
69 |
70 | Event.fireEvent(getTableColumn(), editEvent);
71 | }
72 | // we need to setEditing(false):
73 | super.cancelEdit(); // this fires an invalid EditCancelEvent.
74 | // update the item within this cell, so that it represents the new value
75 | updateItem(newValue, false);
76 | if (table != null) {
77 | // reset the editing cell on the TableView
78 | table.edit(-1, null);
79 | }
80 | }
81 |
82 | /** {@inheritDoc} */
83 | @Override
84 | public void cancelEdit() {
85 | if (escapePressed) {
86 | // this is a cancel event after escape key
87 | super.cancelEdit();
88 | setText(getItemText()); // restore the original text in the view
89 | } else {
90 | // this is not a cancel event after escape key
91 | // we interpret it as commit.
92 | String newText = textField.getText();
93 | // commit the new text to the model
94 | this.commitEdit(getConverter().fromString(newText));
95 | }
96 | setGraphic(null); // stop editing with TextField
97 | }
98 |
99 | /** {@inheritDoc} */
100 | @Override
101 | public void updateItem(T item, boolean empty) {
102 | super.updateItem(item, empty);
103 | updateItem();
104 | }
105 |
106 | private TextField getTextField() {
107 |
108 | final TextField textField = new TextField(getItemText());
109 |
110 | textField.setOnAction(new EventHandler() {
111 |
112 | @Override
113 | public void handle(ActionEvent event) {
114 | System.out.println("hi");
115 | }
116 | });
117 |
118 | // Use onAction here rather than onKeyReleased (with check for Enter),
119 | textField.setOnAction(event -> {
120 | if (getConverter() == null) {
121 | throw new IllegalStateException("StringConverter is null.");
122 | }
123 | this.commitEdit(getConverter().fromString(textField.getText()));
124 | event.consume();
125 | });
126 |
127 | textField.focusedProperty().addListener(new ChangeListener() {
128 | @Override
129 | public void changed(ObservableValue extends Boolean> observable,
130 | Boolean oldValue, Boolean newValue) {
131 | if (!newValue) {
132 | commitEdit(getConverter().fromString(textField.getText()));
133 | }
134 | }
135 | });
136 |
137 | textField.setOnKeyPressed(t -> {
138 | if (t.getCode() == KeyCode.ESCAPE)
139 | escapePressed = true;
140 | else
141 | escapePressed = false;
142 | });
143 | textField.setOnKeyReleased(t -> {
144 | if (t.getCode() == KeyCode.ESCAPE) {
145 | throw new IllegalArgumentException(
146 | "did not expect esc key releases here.");
147 | }
148 | });
149 |
150 | textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
151 | if (event.getCode() == KeyCode.ESCAPE) {
152 | textField.setText(getConverter().toString(getItem()));
153 | cancelEdit();
154 | event.consume();
155 | } else if (event.getCode() == KeyCode.RIGHT
156 | || event.getCode() == KeyCode.TAB) {
157 | getTableView().getSelectionModel().selectNext();
158 | event.consume();
159 | } else if (event.getCode() == KeyCode.LEFT) {
160 | getTableView().getSelectionModel().selectPrevious();
161 | event.consume();
162 | } else if (event.getCode() == KeyCode.UP) {
163 | getTableView().getSelectionModel().selectAboveCell();
164 | event.consume();
165 | } else if (event.getCode() == KeyCode.DOWN) {
166 | getTableView().getSelectionModel().selectBelowCell();
167 | event.consume();
168 | }
169 | });
170 |
171 | return textField;
172 | }
173 |
174 | private String getItemText() {
175 | return getConverter() == null
176 | ? getItem() == null ? "" : getItem().toString()
177 | : getConverter().toString(getItem());
178 | }
179 |
180 | private void updateItem() {
181 | if (isEmpty()) {
182 | setText(null);
183 | setGraphic(null);
184 | } else {
185 | if (isEditing()) {
186 | if (textField != null) {
187 | textField.setText(getItemText());
188 | }
189 | setText(null);
190 | setGraphic(textField);
191 | } else {
192 | setText(getItemText());
193 | setGraphic(null);
194 | }
195 | }
196 | }
197 |
198 | private void startEdit(final TextField textField) {
199 | if (textField != null) {
200 | textField.setText(getItemText());
201 | }
202 | setText(null);
203 | setGraphic(textField);
204 | textField.selectAll();
205 | // requesting focus so that key input can immediately go into the
206 | // TextField
207 | textField.requestFocus();
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/controller/PersonTableData.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.controller;
2 |
3 | import java.util.Date;
4 |
5 | import javafx.beans.property.SimpleDoubleProperty;
6 | import javafx.beans.property.SimpleObjectProperty;
7 | import javafx.beans.property.SimpleStringProperty;
8 | import lankydan.tutorials.fxml.data.Person;
9 |
10 | public class PersonTableData {
11 |
12 | private SimpleStringProperty firstName;
13 | private SimpleStringProperty surname;
14 | private SimpleObjectProperty dateOfBirth;
15 | private SimpleStringProperty occupation;
16 | private SimpleDoubleProperty salary;
17 |
18 | public PersonTableData(Person person) {
19 | this.firstName = new SimpleStringProperty(person.getFirstName());
20 | this.surname = new SimpleStringProperty(person.getSurname());
21 | this.dateOfBirth = new SimpleObjectProperty(
22 | person.getDateOfBirth());
23 | this.occupation = new SimpleStringProperty(person.getOccupation());
24 | this.salary = new SimpleDoubleProperty(person.getSalary());
25 | }
26 |
27 | public PersonTableData(final String firstName, final String surname,
28 | final Date dateOfBirth, final String occupation,
29 | final double salary) {
30 | this.firstName = new SimpleStringProperty(firstName);
31 | this.surname = new SimpleStringProperty(surname);
32 | this.dateOfBirth = new SimpleObjectProperty(dateOfBirth);
33 | this.occupation = new SimpleStringProperty(occupation);
34 | this.salary = new SimpleDoubleProperty(salary);
35 | }
36 |
37 | public String getFirstName() {
38 | return firstName.get();
39 | }
40 |
41 | public void setFirstName(final String firstName) {
42 | this.firstName.set(firstName);
43 | }
44 |
45 | public String getSurname() {
46 | return surname.get();
47 | }
48 |
49 | public void setSurname(final String surname) {
50 | this.surname.set(surname);
51 | }
52 |
53 | public Date getDateOfBirth() {
54 | return dateOfBirth.get();
55 | }
56 |
57 | public void setDateOfBirth(final Date dateOfBirth) {
58 | this.dateOfBirth.set(dateOfBirth);
59 | }
60 |
61 | public String getOccupation() {
62 | return occupation.get();
63 | }
64 |
65 | public void setOccupation(final String occupation) {
66 | this.occupation.set(occupation);
67 | }
68 |
69 | public double getSalary() {
70 | return salary.get();
71 | }
72 |
73 | public void setSalary(final double salary) {
74 | this.salary.set(salary);
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/controller/TableAppController.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.controller;
2 |
3 | import java.net.URL;
4 | import java.text.ParseException;
5 | import java.text.SimpleDateFormat;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.Date;
9 | import java.util.List;
10 | import java.util.ResourceBundle;
11 |
12 | import javafx.beans.property.SimpleObjectProperty;
13 | import javafx.collections.FXCollections;
14 | import javafx.collections.ObservableList;
15 | import javafx.event.ActionEvent;
16 | import javafx.fxml.FXML;
17 | import javafx.fxml.Initializable;
18 | import javafx.scene.control.Button;
19 | import javafx.scene.control.TableColumn;
20 | import javafx.scene.control.TablePosition;
21 | import javafx.scene.control.TableView;
22 | import javafx.scene.control.TextField;
23 | import javafx.scene.input.KeyCode;
24 | import lankydan.tutorials.fxml.cell.EditCell;
25 | import lankydan.tutorials.fxml.converter.MyDateStringConverter;
26 | import lankydan.tutorials.fxml.converter.MyDoubleStringConverter;
27 | import lankydan.tutorials.fxml.data.Person;
28 |
29 | public class TableAppController implements Initializable {
30 |
31 | @FXML
32 | private TableView table;
33 |
34 | @FXML
35 | private TextField firstNameTextField;
36 |
37 | @FXML
38 | private TextField surnameTextField;
39 |
40 | @FXML
41 | private TextField dateOfBirthTextField;
42 |
43 | @FXML
44 | private TextField occupationTextField;
45 |
46 | @FXML
47 | private TextField salaryTextField;
48 |
49 | @FXML
50 | private Button submitButton;
51 |
52 | private ObservableList data = FXCollections
53 | .observableArrayList();
54 |
55 | private static final String DATE_PATTERN = "dd/MM/yyyy";
56 |
57 | private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat(
58 | DATE_PATTERN);
59 |
60 | @FXML
61 | private TableColumn dateOfBirthColumn;
62 |
63 | @FXML
64 | private TableColumn salaryColumn;
65 |
66 | @Override
67 | public void initialize(final URL url, final ResourceBundle rb) {
68 | DATE_FORMATTER.setLenient(false);
69 | table.setItems(data);
70 | populate(retrieveData());
71 | // createColumnManually();
72 | setupDateOfBirthColumn();
73 | setupSalaryColumn();
74 | setTableEditable();
75 |
76 | }
77 |
78 | private List retrieveData() {
79 | try {
80 | return Arrays.asList(
81 | new Person("Dan", "Newton",
82 | DATE_FORMATTER.parse("06/01/1994"),
83 | "Java Developer", 22000),
84 | new Person("George", "Newton",
85 | DATE_FORMATTER.parse("24/01/1995"), "Bro", 15021),
86 | new Person("Laura", "So",
87 | DATE_FORMATTER.parse("24/04/1995"), "Student", 0),
88 | new Person("Jamie", "Harwood",
89 | DATE_FORMATTER.parse("15/12/9999"),
90 | "Java Developer", 30000),
91 | new Person("Michael", "Collins",
92 | DATE_FORMATTER.parse("01/01/0001"), "Developer",
93 | 299),
94 | new Person("Stuart", "Kerrigan",
95 | DATE_FORMATTER.parse("06/10/1894"),
96 | "Teaching Fellow", 100000));
97 | } catch (ParseException e) {
98 | e.printStackTrace();
99 | }
100 | return new ArrayList();
101 | }
102 |
103 | private void populate(final List people) {
104 | people.forEach(p -> data.add(new PersonTableData(p)));
105 | }
106 |
107 | private void setupDateOfBirthColumn() {
108 | // formats the display value to display dates in the form of dd/MM/yyyy
109 | dateOfBirthColumn
110 | .setCellFactory(EditCell.forTableColumn(
111 | new MyDateStringConverter(DATE_PATTERN)));
112 | // updates the dateOfBirth field on the PersonTableData object to the
113 | // committed value
114 | dateOfBirthColumn.setOnEditCommit(event -> {
115 | final Date value = event.getNewValue() != null ? event.getNewValue()
116 | : event.getOldValue();
117 | ((PersonTableData) event.getTableView().getItems()
118 | .get(event.getTablePosition().getRow()))
119 | .setDateOfBirth(value);
120 | table.refresh();
121 | });
122 | }
123 |
124 | private void setupSalaryColumn() {
125 | salaryColumn.setCellFactory(
126 | EditCell.forTableColumn(
127 | new MyDoubleStringConverter()));
128 | // updates the salary field on the PersonTableData object to the
129 | // committed value
130 | salaryColumn.setOnEditCommit(event -> {
131 | final Double value = event.getNewValue() != null
132 | ? event.getNewValue() : event.getOldValue();
133 | ((PersonTableData) event.getTableView().getItems()
134 | .get(event.getTablePosition().getRow())).setSalary(value);
135 | table.refresh();
136 | });
137 | }
138 |
139 | private void setTableEditable() {
140 | table.setEditable(true);
141 | // allows the individual cells to be selected
142 | table.getSelectionModel().cellSelectionEnabledProperty().set(true);
143 | // when character or numbers pressed it will start edit in editable
144 | // fields
145 | table.setOnKeyPressed(event -> {
146 | if (event.getCode().isLetterKey() || event.getCode().isDigitKey()) {
147 | editFocusedCell();
148 | } else if (event.getCode() == KeyCode.RIGHT
149 | || event.getCode() == KeyCode.TAB) {
150 | table.getSelectionModel().selectNext();
151 | event.consume();
152 | } else if (event.getCode() == KeyCode.LEFT) {
153 | // work around due to
154 | // TableView.getSelectionModel().selectPrevious() due to a bug
155 | // stopping it from working on
156 | // the first column in the last row of the table
157 | selectPrevious();
158 | event.consume();
159 | }
160 | });
161 | }
162 |
163 | @SuppressWarnings("unchecked")
164 | private void editFocusedCell() {
165 | final TablePosition focusedCell = table
166 | .focusModelProperty().get().focusedCellProperty().get();
167 | table.edit(focusedCell.getRow(), focusedCell.getTableColumn());
168 | }
169 |
170 | @SuppressWarnings("unchecked")
171 | private void selectPrevious() {
172 | if (table.getSelectionModel().isCellSelectionEnabled()) {
173 | // in cell selection mode, we have to wrap around, going from
174 | // right-to-left, and then wrapping to the end of the previous line
175 | TablePosition pos = table.getFocusModel()
176 | .getFocusedCell();
177 | if (pos.getColumn() - 1 >= 0) {
178 | // go to previous row
179 | table.getSelectionModel().select(pos.getRow(),
180 | getTableColumn(pos.getTableColumn(), -1));
181 | } else if (pos.getRow() < table.getItems().size()) {
182 | // wrap to end of previous row
183 | table.getSelectionModel().select(pos.getRow() - 1,
184 | table.getVisibleLeafColumn(
185 | table.getVisibleLeafColumns().size() - 1));
186 | }
187 | } else {
188 | int focusIndex = table.getFocusModel().getFocusedIndex();
189 | if (focusIndex == -1) {
190 | table.getSelectionModel().select(table.getItems().size() - 1);
191 | } else if (focusIndex > 0) {
192 | table.getSelectionModel().select(focusIndex - 1);
193 | }
194 | }
195 | }
196 |
197 | private TableColumn getTableColumn(
198 | final TableColumn column, int offset) {
199 | int columnIndex = table.getVisibleLeafIndex(column);
200 | int newColumnIndex = columnIndex + offset;
201 | return table.getVisibleLeafColumn(newColumnIndex);
202 | }
203 |
204 | private void createColumnManually() {
205 | TableColumn dateOfBirthColumn = new TableColumn<>(
206 | "Date of Birth");
207 | dateOfBirthColumn.setCellValueFactory(person -> {
208 | SimpleObjectProperty property = new SimpleObjectProperty<>();
209 | property.setValue(person.getValue().getDateOfBirth());
210 | return property;
211 | });
212 | table.getColumns().add(2, dateOfBirthColumn);
213 | }
214 |
215 | @FXML
216 | private void submit(final ActionEvent event) {
217 | if (allFieldsValid()) {
218 | final String firstName = firstNameTextField.getText();
219 | final String surname = surnameTextField.getText();
220 | Date dateOfBirth = null;
221 | try {
222 | dateOfBirth = DATE_FORMATTER
223 | .parse(dateOfBirthTextField.getText());
224 | } catch (final ParseException e) {
225 | }
226 | final String occupation = occupationTextField.getText();
227 | final double salary = Double.parseDouble(salaryTextField.getText());
228 | data.add(new PersonTableData(firstName, surname, dateOfBirth,
229 | occupation, salary));
230 | }
231 | }
232 |
233 | private boolean allFieldsValid() {
234 | return !firstNameTextField.getText().isEmpty()
235 | && !surnameTextField.getText().isEmpty()
236 | && dateOfBirthFieldValid()
237 | && !occupationTextField.getText().isEmpty()
238 | && !salaryTextField.getText().isEmpty();
239 | }
240 |
241 | private boolean dateOfBirthFieldValid() {
242 | if (!dateOfBirthTextField.getText().isEmpty()) {
243 | try {
244 | DATE_FORMATTER.parse(dateOfBirthTextField.getText());
245 | return true;
246 | } catch (ParseException e) {
247 | return false;
248 | }
249 | }
250 | return false;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/converter/MyDateStringConverter.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.converter;
2 |
3 | import java.util.Date;
4 |
5 | import javafx.util.converter.DateStringConverter;
6 |
7 | public class MyDateStringConverter extends DateStringConverter {
8 |
9 | public MyDateStringConverter(final String pattern) {
10 | super(pattern);
11 | }
12 |
13 | @Override
14 | public Date fromString(String value) {
15 | // catches the RuntimeException thrown by
16 | // DateStringConverter.fromString()
17 | try {
18 | return super.fromString(value);
19 | } catch (RuntimeException ex) {
20 | return null;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/converter/MyDoubleStringConverter.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.converter;
2 |
3 | import javafx.util.converter.DoubleStringConverter;
4 |
5 | public class MyDoubleStringConverter extends DoubleStringConverter {
6 |
7 | @Override
8 | public Double fromString(final String value) {
9 | return value.isEmpty() || !isNumber(value) ? null
10 | : super.fromString(value);
11 | }
12 |
13 | public boolean isNumber(String value) {
14 | int size = value.length();
15 | for (int i = 0; i < size; i++) {
16 | if (!Character.isDigit(value.charAt(i))) {
17 | return false;
18 | }
19 | }
20 | return size > 0;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/data/Person.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.data;
2 |
3 | import java.util.Date;
4 |
5 | public class Person {
6 |
7 | private String firstName;
8 | private String surname;
9 | private Date dateOfBirth;
10 | private String occupation;
11 | private double salary;
12 |
13 | public Person(String firstName, String surname, Date dateOfBirth,
14 | String occupation, double salary) {
15 | super();
16 | this.firstName = firstName;
17 | this.surname = surname;
18 | this.dateOfBirth = dateOfBirth;
19 | this.occupation = occupation;
20 | this.salary = salary;
21 | }
22 |
23 | public String getFirstName() {
24 | return firstName;
25 | }
26 |
27 | public void setFirstName(String firstName) {
28 | this.firstName = firstName;
29 | }
30 |
31 | public String getSurname() {
32 | return surname;
33 | }
34 |
35 | public void setSurname(String surname) {
36 | this.surname = surname;
37 | }
38 |
39 | public Date getDateOfBirth() {
40 | return dateOfBirth;
41 | }
42 |
43 | public void setDateOfBirth(Date dateOfBirth) {
44 | this.dateOfBirth = dateOfBirth;
45 | }
46 |
47 | public String getOccupation() {
48 | return occupation;
49 | }
50 |
51 | public void setOccupation(String occupation) {
52 | this.occupation = occupation;
53 | }
54 |
55 | public double getSalary() {
56 | return salary;
57 | }
58 |
59 | public void setSalary(double salary) {
60 | this.salary = salary;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/JavaFXTableTutorial/src/lankydan/tutorials/fxml/launcher/TableAppLauncher.java:
--------------------------------------------------------------------------------
1 | package lankydan.tutorials.fxml.launcher;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Parent;
6 | import javafx.scene.Scene;
7 | import javafx.stage.Stage;
8 |
9 | public class TableAppLauncher extends Application {
10 |
11 | public static void main(String[] args) {
12 | Application.launch(TableAppLauncher.class, args);
13 | }
14 |
15 | @Override
16 | public void start(Stage stage) throws Exception {
17 | try {
18 | final Parent root = FXMLLoader.load(getClass().getClassLoader()
19 | .getResource("lankydan/tutorials/fxml/TableApp.fxml"));
20 | final Scene scene = new Scene(root);
21 | stage.setScene(scene);
22 | stage.setTitle("Table Example");
23 |
24 | stage.setMinHeight(250);
25 | stage.setMinWidth(500);
26 |
27 | stage.setMaxHeight(500);
28 | stage.setMaxWidth(1000);
29 |
30 | stage.show();
31 | } catch (Exception e) {
32 | System.out.print(e);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------