JavaFX表 - 如何添加组件?

Mar*_*ark 7 javafx tableview

我有一个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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:在进一步调查后,我发现了使用预定义的单元格类型(如文本和检查)覆盖单元格图形的示例.目前尚不清楚是否可以将任何通用jfx组件放置在像JFXPanel这样的单元格中.这与JTable不同,因为使用JTable我可以放置任何从JComponent继承的东西,只要我正确设置渲染类.如果有人知道如何(或者甚至可能)将JFXPanel放置在单元格或其他通用JFx组件(如Button)中,那将非常有用.

jew*_*sea 24

您的实施问题

您可以在Swing应用程序中嵌入JavaFX组件(通过将JavaFX组件放在JFXPanel中).但是你不能在JavaFX中嵌入Swing组件(除非你使用的是JavaFX 8+).

JavaFX无论如何都有自己的按钮实现,因此没有理由javax.swing.JButton在JavaFX场景中嵌入,即使您使用的是Java8,它也可以工作.

但这不会解决您的所有问题.您为按钮列提供单元格值工厂以提供按钮,但不提供自定义单元格渲染工厂.默认的表格单元格渲染工厂toString在相应的单元格值上呈现输出,这就是为什么您只是在表格实现中看到按钮的字符串表示形式.

您正在将按钮放在Person对象中.不要那样做 - 他们不属于那里.而是在单元格渲染工厂中动态生成一个按钮.这使您可以利用表的虚拟流技术,它只为您在屏幕上看到的内容创建可视节点,而不是为表的后备数据存储中的每个元素创建.例如,如果屏幕上有10行可见,表中有10000个元素,则只创建10个按钮而不是10000个按钮.

如何解决它

  1. 而不是使用JavaFX Buttonjavax.swing.JButton.
  2. 为您的按钮提供单元格渲染工厂.
  3. 在表格单元格中生成按钮而不是在Person中生成按钮.
  4. 要在表格单元格中设置按钮(或任意JavaFX节点),请使用单元格的setGraphic方法.

正确的示例代码

此代码包含建议的修复程序和一些改进.

礼品登记

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"
    );
}
Run Code Online (Sandbox Code Playgroud)

在JavaFX中使用Swing组件

在进一步调查后,我发现了使用预定义单元格类型(如文本和检查)覆盖单元格图形的示例.目前尚不清楚是否可以将任何通用jfx组件放置在像JFXPanel这样的单元格中.

一个JFXPanel是在Swing不是JavaFX的Swing组件嵌入JavaFX的组件,因此它会使得完全没有意义的尝试放置JFXPanel在一个JavaFX TableView.这就是为什么你找不到任何人尝试这样的事情的例子.

这与JTable不同,因为使用JTable我可以放置任何从JComponent继承的东西,只要我正确设置渲染类.

在这方面,JavaFX TableView类似于Swing JTable.而不是a JComponent,Node是JavaFX的基本构建块 - 它们是类似的,虽然不同.只要为其提供适当的单元工厂,就可以Node在JavaFX中渲染任何TableView内容.

Java 8中,有一个SwingNode可以包含Swing JComponent.A SwingNode允许您在JavaFX TableView中呈现任何Swing组件.

使用a的代码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); }
}
Run Code Online (Sandbox Code Playgroud)

替代实施

基本上我要做的是为我的java项目添加手势滚动支持,这样用户就可以"轻弹"表格和标签

尽管Java 8应该允许您实现您想要的功能,但如果您更喜欢使用较旧的Java版本,则可以使用基于Swing的Multitouch for Java(MT4J)系统代替JavaFX.