我有一个swing项目,它使用许多JTable来显示从文本到面板到混合按钮和复选框的各种事物。我能够通过覆盖表格单元格渲染器来返回通用JComponents来做到这一点。我的问题是可以使用JavaFx制作类似的表吗?
我想更新项目中的所有表格,以便使用JavaFx来支持手势。似乎TableView是要使用的JavaFx组件,我尝试添加按钮,但是在显示时它显示按钮的字符串值,而不是按钮本身。看起来我必须覆盖行工厂或单元工厂来做我想要的但是没有很多例子。这是我用作示例的代码,它将按钮显示为字符串。
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;
}
}
}
编辑:在进一步调查后,我发现了使用预定义单元格类型(如文本和检查)覆盖单元格图形的示例。目前尚不清楚是否可以将任何通用jfx组件放置在像JFXPanel这样的单元格中。这与JTable不同,因为使用JTable我可以放置任何从JComponent继承的东西,只要我正确设置渲染类。如果有人知道如何(或者甚至可能)将JFXPanel放在单元格或其他通用JFx组件(如Button)中,那将非常有帮助。
答案 0 :(得分:24)
您的实施问题
您可以在Swing应用程序中嵌入JavaFX组件(通过将JavaFX组件放在JFXPanel中)。但是你不能在JavaFX中嵌入Swing组件(除非你使用的是JavaFX 8 +)。
JavaFX无论如何都有它自己的按钮实现,因此没有理由在JavaFX场景中嵌入javax.swing.JButton
,即使你使用的是Java8也可以使用它。
但这并不能解决你所有的问题。您为按钮列提供单元格值工厂以提供按钮,但不提供自定义单元格渲染工厂。默认的表格单元格渲染工厂在相应的单元格值上呈现toString
输出,这就是您在表格实现中看到按钮的字符串表示的原因。
您将按钮放在Person
对象中。不要那样做 - 他们不属于那里。而是在单元格渲染工厂中动态生成一个按钮。这使您可以利用表的虚拟流技术,它只为您在屏幕上看到的内容创建可视节点,而不是为表的后备数据存储中的每个元素创建。例如,如果屏幕上有10行可见,表格中有10000个元素,则只会创建10个按钮而不是10000个。
如何修复
javax.swing.JButton
。更正示例代码
此代码包含建议的修复程序和一些改进。
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"
);
}
在JavaFX中使用Swing组件
在进一步调查后,我发现使用预定义的单元格类型(如文本和检查)覆盖单元格图形的示例。目前尚不清楚是否可以将任何通用jfx组件放置在像JFXPanel这样的单元格中。
JFXPanel用于在Swing中嵌入JavaFX组件而不是JavaFX中的Swing组件,因此尝试将JFXPanel
放在JavaFX TableView
中是完全没有意义的。这就是为什么你找不到任何人试图这样做的例子。
这与JTable不同,因为使用JTable我可以放置任何从JComponent继承的东西,只要我正确设置渲染类。
在这方面,JavaFX TableView
类似于Swing JTable
。 Node不是JComponent
,而是JavaFX的基本构建块 - 它们是类似的,虽然不同。只要为其提供适当的单元工厂,就可以在JavaFX Node
中呈现任何TableView
。
在Java 8中,有一个SwingNode
可以包含Swing JComponent
。 SwingNode
允许您在JavaFX TableView中呈现任何Swing组件。
使用SwingNode
的代码非常简单:
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); }
}
替代实施
基本上我要做的是为我的java项目添加手势滚动支持,这样用户就可以“轻弹”表格和标签
虽然Java 8应该允许您实现您想要的功能,但如果您更喜欢使用较旧的Java版本,则可以使用基于Swing的Multitouch for Java (MT4J)系统代替JavaFX。