8

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.

4

1 に答える 1

27

実装に関する問題

JavaFX コンポーネントを Swing アプリケーションに埋め込むことができます (JavaFX コンポーネントをJFXPanelに配置することにより)。ただし、Swing コンポーネントを JavaFX に埋め込むことはできません (JavaFX 8 以降を使用している場合を除く)。

javax.swing.JButtonとにかくJavaFXには独自のボタン実装があるため、Java8を使用していて機能する場合でも、JavaFXシーンに埋め込む理由はありません。

しかし、それですべての問題が解決するわけではありません。ボタン列にボタンを提供するセル値ファクトリを提供していますが、カスタム セル レンダリング ファクトリは提供していません。デフォルトのテーブル セル レンダリング ファクトリはtoString、それぞれのセル値で出力をレンダリングします。これが、テーブルの実装でボタンの to 文字列表現が表示される理由です。

オブジェクトにボタンを配置していPersonます。そうしないでください - 彼らはそこに属していません。代わりに、セル レンダリング ファクトリでボタンを動的に生成します。これにより、テーブルの仮想フロー テクノロジを利用して、テーブルのバッキング データ ストア内のすべての要素ではなく、画面に表示されるものに対してのみビジュアル ノードを作成できます。たとえば、画面に 10 行が表示され、テーブルに 10000 の要素がある場合、10000 ではなく 10 個のボタンのみが作成されます。

修正方法

  1. の代わりにJavaFXボタンjavax.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"
    );
}

JavaFX での Swing コンポーネントの使用について

さらに調査した結果、テキストやチェックなどの事前定義されたセル タイプを使用してセル グラフィックをオーバーライドする例を見つけました。一般的な jfx コンポーネントを JFXPanel のようなセルに配置できるかどうかは明確ではありません。

JFXPanelは、JavaFX の Swing コンポーネントではなく、Swing に JavaFX コンポーネントを埋め込むためのものであるためJFXPanel、 JavaFX に aを配置しようとしてもまったく意味がありませんTableView。これが、そのようなことを試みた例が見当たらない理由です。

これは JTable とは異なります。なぜなら、JTable を使用すると、レンダリング クラスを正しくセットアップする限り、JComponent から継承するものなら何でも配置できるからです。

この点で、 JavaFXTableViewは SwingJTableに似ています。の代わりにJComponentNodeは JavaFX の基本的な構成要素です。これらは類似していますが、異なります。適切なセルファクトリを提供する限り Node、JavaFX で任意のものをレンダリングできます。TableView

Java 8には、SwingNodeSwing を含むことができる がありますJComponent。を使用する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); }
}

代替実装

基本的に私がやろうとしているのは、Java プロジェクトにジェスチャ スクロール サポートを追加して、ユーザーがテーブルとタブを「フリック」できるようにすることです。

Java 8 を使用すると、目的を正確に達成できるはずですが、古い Java バージョンを使用する場合は、JavaFX の代わりに Swing ベースのMultitouch for Java (MT4J)システムを使用できます。

于 2013-05-03T19:59:45.747 に答える