JavaFX 表-如何添加组件? [英] JavaFX table- how to add components?

查看:35
本文介绍了JavaFX 表-如何添加组件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Swing 项目,它使用许多 JTable 来显示各种内容,从文本到面板,再到按钮和复选框的混合.我能够通过覆盖表格单元渲染器来返回通用 JComponents 来做到这一点.我的问题是可以使用 JavaFx 制作类似的表格吗?

我想更新我在项目中的所有表,以使用 JavaFx 来主要支持手势.似乎 TableView 是要使用的 JavaFx 组件,我尝试向它添加按钮,但在显示时它显示按钮的字符串值,而不是按钮本身.看起来我必须覆盖行工厂或单元工厂才能做我想做的事,但没有很多例子.这是我用作示例的代码,将按钮显示为字符串.

import javax.swing.JButton;导入 javafx.application.Application;导入 javafx.beans.property.SimpleStringProperty;导入 javafx.collections.FXCollections;导入 javafx.collections.ObservableList;导入 javafx.geometry.Insets;导入 javafx.scene.Group;导入 javafx.scene.Scene;导入 javafx.scene.control.Label;导入 javafx.scene.control.TableColumn;导入 javafx.scene.control.TableView;导入 javafx.scene.control.TextField;导入 javafx.scene.control.cell.PropertyValueFactory;导入 javafx.scene.layout.VBox;导入 javafx.scene.text.Font;导入 javafx.stage.Stage;公共类 GestureEvents 扩展应用程序 {私有 TableViewtable = new TableView();私有最终 ObservableList数据 =FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith@example.com","The Button"),new Person("Isabella", "Johnson", "isabella.johnson@example.com","按钮"),new Person("Ethan", "Williams", "ethan.williams@example.com","The Button"),new Person("Emma", "Jones", "emma.jones@example.com","按钮"),新人(迈克尔",布朗",michael.brown@example.com",按钮"));公共静态无效主(字符串 [] args){发射(参数);}@覆盖公共无效开始(阶段阶段){场景场景=新场景(新组());stage.setTitle("表格视图示例");stage.setWidth(450);stage.setHeight(500);final Label label = new Label("地址簿");label.setFont(new Font("Arial", 20));table.setEditable(true);TableColumn firstNameCol = new TableColumn("名字");firstNameCol.setMinWidth(100);firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));TableColumn lastNameCol = new TableColumn("Last Name");lastNameCol.setMinWidth(100);lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));TableColumn emailCol = new TableColumn("Email");emailCol.setMinWidth(200);emailCol.setCellValueFactory(new PropertyValueFactory("email"));TableColumn btnCol = new TableColumn("Buttons");btnCol.setMinWidth(100);btnCol.setCellValueFactory(new PropertyValueFactory("btn"));table.setItems(data);table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, btnCol);最终 VBox vbox = new VBox();vbox.setSpacing(5);vbox.setPadding(new Insets(10, 0, 0, 10));vbox.getChildren().addAll(label, table);((组)scene.getRoot()).getChildren().addAll(vbox);stage.setScene(场景);舞台表演();}公共静态类人{私有最终 SimpleStringProperty firstName;私有最终 SimpleStringProperty 姓氏;私人最终 SimpleStringProperty 电子邮件;私人最终 JButton btn;私人人(字符串 fName,字符串 lName,字符串电子邮件,字符串 btn){this.firstName = new SimpleStringProperty(fName);this.lastName = new SimpleStringProperty(lName);this.email = new SimpleStringProperty(email);this.btn = new JButton(btn);}公共字符串 getFirstName() {返回 firstName.get();}public void setFirstName(String fName) {firstName.set(fName);}公共字符串 getLastName() {返回 lastName.get();}public void setLastName(String fName) {lastName.set(fName);}公共字符串 getEmail() {返回 email.get();}public void setEmail(String fName) {email.set(fName);}公共 JButton getBtn(){返回 btn;}公共无效 setBtn(字符串 btn){}}公共静态类 ButtonPerson{私人最终 JButton btn;私人按钮人(){btn = new JButton("按钮");}公共 JButton getButton(){返回 btn;}}}

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

解决方案

实施中的问题

您可以在 Swing 应用程序中嵌入 JavaFX 组件(通过将 JavaFX 组件放在 用于在 Swing 中嵌入 JavaFX 组件,而不是JavaFX 中的 Swing 组件,因此尝试在 JavaFX TableView 中放置 JFXPanel 绝对没有意义.这就是为什么你找不到任何人尝试这种事情的例子.

<块引用>

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

JavaFX TableView 在这方面类似于 Swing JTable.代替 JComponentNode 是JavaFX 的基本构建块 - 它们是类似的,但不同.您可以在 JavaFX TableView 中渲染任何 Node,只要为其提供适当的单元工厂即可.

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

使用 SwingNode 的代码非常简单:

import javafx.application.Application;导入 javafx.embed.swing.SwingNode;导入 javafx.scene.Scene;导入 javafx.scene.layout.StackPane;导入 javafx.stage.Stage;导入 javax.swing.*;公共类 SwingFx 扩展应用程序 {@Override public void start(Stage stage) {SwingNode swingNode = new SwingNode();SwingUtilities.invokeLater(() -> swingNode.setContent(new JButton("点击我!")));stage.setScene(new Scene(new StackPane(swingNode), 100, 50));舞台表演();}公共静态无效主(字符串 [] args){ 启动(SwingFx.class);}}

替代实现

<块引用>

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

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

I have a swing project that uses many JTables to display all sorts of things from text to panels to a mix of buttons and check boxes. I was able to do this by overwriting the table cell renderer to return generic JComponents. My question is can a similar table be made using JavaFx?

I want to update all my tables in the project to use JavaFx to support gestures mostly. It seems that TableView is the JavaFx component to use and I tried adding buttons to it but when displayed it shows the string value of the button, not the button itself. It looks like I have to overwrite the row factory or cell factory to do what I want but there are not a lot of 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 investigating further I've found examples that override cell graphics using predefined cell types like text and checks. It is not clear if any generic jfx component can be placed in a cell like a JFXPanel. This is unlike JTable, since using a JTable I can place anything that inherits from JComponent as long as I setup the render class correctly. If someone knows how (or if it's even possible) to place a JFXPanel in a cell or a other generic JFx component like a Button that would be very helpful.

解决方案

Issues with your Implementation

You can embed JavaFX components in Swing applications (by placing the JavaFX component in a JFXPanel). But you can't embed a Swing component in JavaFX (unless you are using JavaFX 8+).

JavaFX has it's own button implementation anyway, so there is no reason to embed a javax.swing.JButton on a JavaFX scene, even if you were using Java8 and it would work.

But that won't fix all of your issues. You are providing a cell value factory for your button column to supply buttons, but not supplying a custom cell rendering factory. The default table cell rendering factory renders the toString output on the respective cell value, which is why you just see the to string representation of the button in your table implementation.

You are putting buttons in your Person object. Don't do that - they don't belong there. Instead, dynamically generate a button in the cell rendering factory. This allows you to take advantage of a table's virtual flow technology whereby it only creates visual nodes for what you can see on the screen, not for every element in the table's backing data store. For example, if there are 10 rows visible on the screen and 10000 elements in the table, only 10 buttons will be created rather than 10000.

How to fix it

  1. Use JavaFX Button instead instead of javax.swing.JButton.
  2. Provide a cell rendering factory for your button.
  3. Generate a button in the table cell rather than in the Person.
  4. To set the button (or any arbitrary JavaFX node) in the table cell, use the cell's setGraphic method.

Correct Sample Code

This code incorporates the suggested fixes and a couple of improvements.

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"
    );
}

On Using Swing Components in JavaFX

After investigating further I've found examples that override cell graphics using predefined cell types like text and checks. It is not clear if any generic jfx component can be placed in a cell like a JFXPanel.

A JFXPanel is for embedding a JavaFX component in Swing not a Swing component in JavaFX, hence it would make absolutely no sense to try to place a JFXPanel in a JavaFX TableView. This is why you find no examples of anybody attempting such a thing.

This is unlike JTable, since using a JTable I can place anything that inherits from JComponent as long as I setup the render class correctly.

A JavaFX TableView is similar to a Swing JTable in this regard. Instead of a JComponent, a Node is the basic building block for JavaFX - they are analogous, though different. You can render any Node in a JavaFX TableView as long as you supply the appropriate cell factory for it.

In Java 8, there is a SwingNode that can contain a Swing JComponent. A SwingNode allows you to render any Swing component inside a JavaFX TableView.

The code to use a SwingNode is 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 'flick' through the tables and tabs

Though Java 8 should allow you to achieve exactly what you want, if you prefer to use an older Java version, you could use the Swing based Multitouch for Java (MT4J) system for this instead of JavaFX.

这篇关于JavaFX 表-如何添加组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆