2013-05-03 48 views
6

我有一個swing項目,它使用很多JTable來顯示從文本到面板的各種各樣的東西到混合的按鈕和複選框。我可以通過覆蓋表格單元格渲染器來返回通用的JComponents。我的問題是可以使用JavaFx製作類似的表格嗎?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", "[email protected]","The Button"), 
      new Person("Isabella", "Johnson", "[email protected]","The Button"), 
      new Person("Ethan", "Williams", "[email protected]","The Button"), 
      new Person("Emma", "Jones", "[email protected]","The Button"), 
      new Person("Michael", "Brown", "[email protected]","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)中,這將非常有用。

回答

24

問題與實現

您可以嵌入在Swing應用程序JavaFX組件(通過將JavaFX的組件在JFXPanel)。但是你不能在JavaFX中嵌入Swing組件(除非你使用的是JavaFX 8+)。

無論如何JavaFX都有自己的按鈕實現,所以沒有理由在JavaFX場景中嵌入javax.swing.JButton,即使您使用的是Java8並且它可以工作。

但這並不能解決所有問題。您正在爲按鈕列提供單元格值工廠來提供按鈕,但不提供自定義單元格渲染工廠。默認表格單元格渲染工廠在相應的單元格值上呈現toString輸出,這就是爲什麼您只能在表格實現中看到按鈕的字符串表示形式。

您正在將按鈕放入您的Person對象中。不要那樣做 - 他們不屬於那裏。相反,在單元格渲染工廠中動態生成一個按鈕。這使您可以利用表格的虛擬流技術,從而只創建可在屏幕上看到的視覺節點,而不是表格的後備數據存儲中的每個元素。例如,如果有10分排在屏幕上可見,並在表10000元,只有10個按鈕將被創建,而不是10000

如何修復

  1. 使用JavaFX的Button,而不是代替javax.swing.JButton
  2. 爲您的按鈕提供一個單元渲染工廠。
  3. 在表格單元格中生成一個按鈕,而不是在Person中。
  4. 要設置表格單元格中的按鈕(或任意JavaFX節點),請使用單元格的setGraphic方法。

正確的樣本代碼

該規範包含了建議的修復和一對夫婦的改進。

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", "[email protected]","Coffee"), 
      new Person("Isabella", "Johnson", "[email protected]","Fruit"), 
      new Person("Ethan", "Williams", "[email protected]","Fruit"), 
      new Person("Emma", "Jones", "[email protected]","Coffee"), 
      new Person("Michael", "Brown", "[email protected]","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的Swing組件嵌入JavaFX的組件,因此它會使得完全沒有意義的嘗試把一個JFXPanel在一個JavaFX TableView。這就是爲什麼你找不到任何試圖這樣的事情的例子。

這與JTable不同,因爲使用JTable,只要我正確設置渲染類,就可以放置從JComponent繼承的任何東西。

JavaFX TableView在這方面與Swing JTable類似。而不是JComponentNode是JavaFX的基本構建模塊 - 它們雖然不同,但類似。只要您爲其提供適當的單元工廠,就可以在JavaFX TableView中渲染任何Node

Java 8,有一個SwingNode可以包含一個Swing JComponent。 A 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。

+0

非常感謝你的例子。但正如你所說,我不能在Swing組件中嵌入javafx。因此,我現在放在我的jtables上的所有組件都將被重寫。聲音正確嗎?在這種情況下,我不認爲javafx表格視圖對我有用。 基本上我想要做的是添加手勢滾動支持到我的Java項目,所以用戶可以'輕彈'通過表和標籤,我認爲,因爲javafx有本質上的支持,這將是一個好主意,升級項目到javafx。但是如果不重寫許多組件,看起來這是不可能的。 – Mark 2013-05-07 19:54:08

+1

你的假設並不完全正確。我更新了我的答案,以幫助澄清我對此的看法。 – jewelsea 2013-05-08 00:32:19

+0

謝謝...你的食譜 – 2013-07-02 07:12:50