JavaFX table - how to add components?

I have a swing project that uses a lot of JTables to display all kinds of things from text to panels to a combination of buttons and checkboxes. I was able to do this by overwriting the table cell handler to return generic JComponents. My question is: can a similar table be made using JavaFx?

I want to update all my tables in a project to use JavaFx to support gestures basically. TableView seems to be the JavaFx component to use, and I tried adding buttons to it, but when it is displayed, the string value of the button is displayed, not the button itself. It seems like I have to rewrite the factory or cell factory line to do what I want, but there are not many examples. Here is the code I used as an example that displays the button as a string.

import javax.swing.JButton; import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; 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 javafx.scene.text.Font; import javafx.stage.Stage; public class GestureEvents extends Application { private TableView<Person> table = new TableView<Person>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", " jacob.smith@example.com ","The Button"), new Person("Isabella", "Johnson", " isabella.johnson@example.com ","The Button"), new Person("Ethan", "Williams", " ethan.williams@example.com ","The Button"), new Person("Emma", "Jones", " emma.jones@example.com ","The Button"), new Person("Michael", "Brown", " michael.brown@example.com ","The Button") ); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(450); stage.setHeight(500); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); TableColumn firstNameCol = new TableColumn("First Name"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory( new PropertyValueFactory<Person, String>("firstName")); TableColumn lastNameCol = new TableColumn("Last Name"); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory( new PropertyValueFactory<Person, String>("lastName")); TableColumn emailCol = new TableColumn("Email"); emailCol.setMinWidth(200); emailCol.setCellValueFactory( new PropertyValueFactory<Person, String>("email")); TableColumn btnCol = new TableColumn("Buttons"); btnCol.setMinWidth(100); btnCol.setCellValueFactory( new PropertyValueFactory<Person, String>("btn")); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, btnCol); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private final JButton btn; private Person(String fName, String lName, String email, String btn) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); this.btn = new JButton(btn); } 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); } public JButton getBtn(){ return btn; } public void setBtn(String btn){ } } public static class ButtonPerson{ private final JButton btn; private ButtonPerson(){ btn = new JButton("The Button"); } public JButton getButton(){ return btn; } } } 

Edit: After exploring further, I found examples that override cell graphics using predefined cell types such as text and checks. It is unclear whether any common jfx component can be placed in a cell, such as a JFXPanel. This is not like JTable, because with JTable, I can post anything that inherits from JComponent if I set up the visualization class correctly. If someone knows how (or if possible) to place a JFXPanel in a cell or other common JFx component like Button, that would be very helpful.

+6
source share
1 answer

Problems with your implementation

You can embed JavaFX components in Swing applications (by hosting the JavaFX component in the JFXPanel ). But you cannot embed the Swing component in JavaFX (unless you are using JavaFX 8 +).

JavaFX still has its own implementation of buttons, so it makes no sense to embed javax.swing.JButton in the JavaFX scene, even if you use Java8, and that will work.

But this will not solve all your problems. You provide a factory cell value for a button column to serve buttons, but not to provide custom rendering of factory cells. Displaying a table cell by default factory outputs the output of toString to the corresponding cell value, so you just see the string representation of the button in the table implementation.

You put buttons in your Person object. Do not do this - they do not belong there. Instead, dynamically create a button in a factory cell rendering. This allows you to use the technology of virtual table flow, in which it creates only visual nodes for what you can see on the screen, and not for each element in the table data warehouse. For example, if there are 10 rows and 10,000 elements in the table on the screen, only 10 buttons will be created, not 10,000.

How to fix it

  • Instead of javax.swing.JButton instead of javax.swing.JButton use the JavaFX Button .
  • Provide a factory cell selection for your button.
  • Create a button in a table cell, not in Person.
  • To set a button (or any arbitrary JavaFX node) in a table cell, use the setGraphic method.

The correct code example

This code contains suggested fixes and a couple of improvements.

gift registry

 import javafx.application.Application; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; import javafx.collections.*; import javafx.event.*; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.TableColumn.CellDataFeatures; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.*; import javafx.scene.layout.*; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.Callback; public class GestureEvents extends Application { private TableView<Person> table = new TableView<Person>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", " jacob.smith@example.com ","Coffee"), new Person("Isabella", "Johnson", " isabella.johnson@example.com ","Fruit"), new Person("Ethan", "Williams", " ethan.williams@example.com ","Fruit"), new Person("Emma", "Jones", " emma.jones@example.com ","Coffee"), new Person("Michael", "Brown", " michael.brown@example.com ","Fruit") ); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { stage.setTitle("Table View Sample"); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); final Label actionTaken = new Label(); table.setEditable(true); TableColumn firstNameCol = new TableColumn("First Name"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory( new PropertyValueFactory<Person, String>("firstName")); TableColumn lastNameCol = new TableColumn("Last Name"); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory( new PropertyValueFactory<Person, String>("lastName")); TableColumn emailCol = new TableColumn("Email"); emailCol.setMinWidth(200); emailCol.setCellValueFactory( new PropertyValueFactory<Person, String>("email")); TableColumn<Person, Person> btnCol = new TableColumn<>("Gifts"); btnCol.setMinWidth(150); btnCol.setCellValueFactory(new Callback<CellDataFeatures<Person, Person>, ObservableValue<Person>>() { @Override public ObservableValue<Person> call(CellDataFeatures<Person, Person> features) { return new ReadOnlyObjectWrapper(features.getValue()); } }); btnCol.setComparator(new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.getLikes().compareTo(p2.getLikes()); } }); btnCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() { @Override public TableCell<Person, Person> call(TableColumn<Person, Person> btnCol) { return new TableCell<Person, Person>() { final ImageView buttonGraphic = new ImageView(); final Button button = new Button(); { button.setGraphic(buttonGraphic); button.setMinWidth(130); } @Override public void updateItem(final Person person, boolean empty) { super.updateItem(person, empty); if (person != null) { switch (person.getLikes().toLowerCase()) { case "fruit": button.setText("Buy fruit"); buttonGraphic.setImage(fruitImage); break; default: button.setText("Buy coffee"); buttonGraphic.setImage(coffeeImage); break; } setGraphic(button); button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { actionTaken.setText("Bought " + person.getLikes().toLowerCase() + " for: " + person.getFirstName() + " " + person.getLastName()); } }); } else { setGraphic(null); } } }; } }); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, btnCol); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 10, 10, 10)); vbox.getChildren().addAll(label, table, actionTaken); VBox.setVgrow(table, Priority.ALWAYS); stage.setScene(new Scene(vbox)); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private final SimpleStringProperty likes; private Person(String fName, String lName, String email, String likes) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); this.likes = new SimpleStringProperty(likes); } 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); } public String getLikes() { return likes.get(); } public void setLikes(String likes) { this.likes.set(likes); } } // icons for non-commercial use with attribution from: http://www.iconarchive.com/show/veggies-icons-by-iconicon/bananas-icon.html and http://www.iconarchive.com/show/collection-icons-by-archigraphs.html private final Image coffeeImage = new Image( "http://icons.iconarchive.com/icons/archigraphs/collection/48/Coffee-icon.png" ); private final Image fruitImage = new Image( "http://icons.iconarchive.com/icons/iconicon/veggies/48/bananas-icon.png" ); } 

When using Swing components in JavaFX

After further study, I found examples that override cell graphics using predefined cell types, such as text and checks. It is unclear whether any generic jfx component can be placed in a cell, such as a JFXPanel.

A JFXPanel is designed to embed a JavaFX component in Swing, not a Swing component in JavaFX, so it would be completely futile to try to place a JFXPanel in a JavaFX TableView . That is why you do not find examples of anyone trying to do something.

This is not like JTable, because with JTable I can post anything that inherits from JComponent if I set up the visualization class correctly.

The JavaFX TableView in this regard is similar to Swing JTable . Instead of JComponent Node is the main building block for JavaFX β€” they are similar, albeit different. You can map any Node to the JavaFX TableView , as long as you provide the appropriate factory cell for it.

In Java 8, there is a SwingNode that may contain a Swing JComponent . A SwingNode allows you to display any Swing component in a JavaView TableView.

The code for using SwingNode very simple:

 import javafx.application.Application; import javafx.embed.swing.SwingNode; import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import javax.swing.*; public class SwingFx extends Application { @Override public void start(Stage stage) { SwingNode swingNode = new SwingNode(); SwingUtilities.invokeLater(() -> swingNode.setContent(new JButton("Click me!"))); stage.setScene(new Scene(new StackPane(swingNode), 100, 50)); stage.show(); } public static void main(String[] args) { launch(SwingFx.class); } } 

Alternative implementation

Basically what I'm trying to do is add gesture scrolling support to my java project so the user can β€œclick” on tables and tabs

Despite the fact that Java 8 will allow you to achieve exactly what you want, if you prefer to use an older version of Java, you can use SwaS Multitouch for Java (MT4J) for this instead of JavaFX.

+24
source

Source: https://habr.com/ru/post/944217/


All Articles