2017-03-16 79 views
0

可重複使用的方法,我在下面的方法我的JavaFX應用程序了:列格式化

columExample.setCellFactory((TableColumn<Person, Person> column) -> { 
     return new TableCell<Peson, Person>() { 
      @Override 
      protected void updateItem(Person item, boolean empty) { 
       super.updateItem(item, empty); 
       if (item == null || empty) { 
        setText(null); 
       } else { 
        setText(item.getPerson_name()); 
       } 
      } 
     }; 
    }); 

現在這個工程確定,怎麼過,我想使這個可重複使用的,我有例如模型是圖書,並有不同的獲得者,那麼這變得不可用。

我能做些什麼來使一種方法,我可以申請任何模型對象。我只是爲我的GUI使用fxml文件,並且我將hibernate與使用非屬性值的模型一起使用(雖然我可以切換,因爲我只是測試);但是,

我以這種方式構建我的數據; 我使用hql查詢,我後來添加到可觀察列表中,然後我將該可觀察列表放在tableview;

tableview.setItems(someObservableList); 

,並在我的初始化方法,我有

columnX.setCellValueFactory(new PropertyValueFactory("name")); 

    @FXML 
TableColumn columnX; 

在這一點上,我有我的所有列填充數據。現在這個棘手的部分,我的模型類的所有原始數據都顯示正常,但是對象顯示爲字符串方法。而這就是爲什麼我使用的方法,我先掛

columExample.setCellFactory((TableColumn<Person, Person> column) -> { 
    return new TableCell<Peson, Person>() { 
     @Override 
     protected void updateItem(Person item, boolean empty) { 
      super.updateItem(item, empty); 
      if (item == null || empty) { 
       setText(null); 
      } else { 
       setText(item.getPerson_name()); 
      } 
     } 
    }; 
}); 

,這樣我剛剛得到的人的名字,而不是整個字符串的方法。

那麼,有沒有簡單的方法,我可以做到這一點

TableColumn<Person, String> columnExample = new TableColumn<>("Name"); 
columnExample.setCellValueFactory(cellData -> 
new SimpleStringProperty(cellData.getValue().getPersonName())); 

,因爲我不能用這個在我的當前設置,我的程序只顯示錯誤在應用程序啓動。(我假設,因爲這創造了新的tablecolum和我希望將它應用於我當前的@FXML列)。

乾杯

+0

爲什麼你在這種情況下使用細胞工廠,而不是細胞價值工廠? –

+0

顯示什麼錯誤? –

+0

好的,你的答案幫了我很大的忙,我得到的錯誤是一些問題,在我把桌子扔掉之後解決了。感謝你! – ImRaphael

回答

2

在你舉的情況下,你應該使用單元格值工廠:

TableColumn<Person, String> columnExample = new TableColumn<>("Name"); 

columnExample.setCellValueFactory(cellData -> 
    new SimpleStringProperty(cellData.getValue().getPersonName())); 

然後默認細胞工廠會做你想要什麼。

如果使用JavaFX property pattern實現模型,即你有

public class Person { 

    private final StringProperty personName = new SimpleStringProperty(); 

    public StringProperty personNameProperty() { 
     return personName ; 
    } 

    public final String getPersonName() { 
     return personNameProperty().get(); 
    } 

    public final void setPersonName(String name) { 
     personNameProperty().set(name); 
    } 
} 

那麼這將成爲

TableColumn<Person, String> columnExample = new TableColumn<>("Name"); 
columnExample.setCellValueFactory(cellData -> cellData.getValue().personNameProperty()); 

我經常使用的工具方法用於創建列,以減少代碼。基本的想法是編寫一個方法,將不同的事物參數化。在前面的兩個線路,即根據不同要創建的列的事情是:

  1. 文中的例子列("Name")和
  2. 行項目去功能Person實例)到包含要顯示的值的屬性。

第一個很容易:它只是一個String。第二個有點棘手:您可以將其表示爲Function<Person, StringProperty>,並將其定義爲lambda表達式p -> p.personNameProperty()或方法參考Person::personNameProperty

這裏的問題當然是,並非每一列都將在Person類型的TableView中,並且並非每一列都將具有String作爲其類型。所以我們也需要參數化這些。這可以通過定義具有兩個類型參數的通用方法來完成。我們用類型參數替代Person,例如SString通過第二類型參數(例如, T。然後我們創建一個TableColumn<S,T>,我們需要一個Function<S, Property<T>>。因此,該方法是這樣的:

private <S,T> TableColumn<S,T> column(String text, Function<S, Property<T>> prop) { 
    TableColumn<S,T> col = new TableColumn<>(text); 
    col.setCellValueFactory(cellData -> prop.apply(cellData.getValue())); 
    return col ; 
} 

您可以

TableColumn<Person, String> columnExample = this.<Person, String>column("Name", Person::personNameProperty); 

稱這在Java 8中,編譯器可以推斷泛型類型的方法(基本上,它看到Person::personNamePropertyFunction<Person, Property<String>>的,因此它推斷SPersonTString),所以你可以這樣做:

TableColumn<Person, String> columnExample = column("Name", Person::personNameProperty); 

我經常發現,在此設置我不需要除了將它傳遞給表中的列,因此代碼往往最終看起來像

table.getColumns().add(column("Name", Person::personNameProperty)); 

,如果你可以使用類似的技術真的確實需要一種生成細胞工廠的方法。在您的實例不同的事情是:

  1. 類型的行中的項目,並在細胞
  2. ,從該項目的小區(即從項目傳遞給獲得一個String功能該updateItem(...)法)

因此,第一個是通過創建一個通用的方法,<S,T>返回Callback<TableColumn<S,T>, TableCell<S,T>>解決。二是通過使用Function<T, String>類型的參數映射電池項目(類型T)解決的String

public <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> 
     cellFactory(Function<T, String> textExtractor) { 

    return col -> new TableCell<S,T>() { 
     @Override 
     protected void updateItem(T item, boolean empty) { 
      super.updateItem(item, empty) ; 
      if (empty || item == null) { 
       setText(null); 
      } else { 
       setText(textExtractor.apply(item)); 
      } 
     } 
    }; 
} 

,然後你原來的例子變得

columnExample.setCellFactory(cellFactory(Person::getPersonName)); 

最後,如果你需要的話,你可以結合所有這些想法。假設你有一個Address類:

public class Address { 

    private String street ; 
    private String city ; 

    // etc etc 

    public String shortTextForm() { 
     return street+" ,"+city; 
    } 

    // ... 
} 

Person類:

public class Person { 

    private final StringProperty name = new SimpleStringProperty(); 

    public StringProperty nameProperty() { 
     return name ; 
    } 

    public final String getName() { 
     return nameProperty().get(); 
    } 

    public final void setName(String name) { 
     nameProperty().set(name); 
    } 

    private final ObjectProperty<Address> address = new SimpleObjectProperty<>(); 

    public ObjectProperty<Address> addressProperty() { 
     return address ; 
    } 

    public Address getAddress() { 
     return addressProperty().get(); 
    } 

    public void setAddress(Address address) { 
     addressProperty().set(address); 
    } 

    // other properties... 
} 

現在創建用於創建列的可重複使用的方法。這一次是要同時設置了cellValueFactory(所以它需要一個Function<S,Property<T>>)和cellFactory(所以它需要一個Function<T, String>):

private <S,T> TableColumn<S,T> column(String text, Function<S,Property<T>> prop, 
    Function<T,String> textExtractor) { 

    TableColumn<S,T> col = new TableColumn<>(text); 
    col.setCellValueFactory(cellData -> prop.apply(cellData.getValue())); 
    col.setCellFactory(c -> new TableCell<S,T>() { 
     @Override 
     protected void updateItem(T item, boolean empty) { 
      super.updateItem(item, empty); 
      if (empty || item == null) { 
       setText(null); 
      } else { 
       setText(textExtractor.apply(item)); 
      } 
     } 
    }); 
    return col ; 
} 

,然後你只是做

TableView<Person> table = new TableView<>(); 
table.getColumns().add(column("Name", Person::nameProperty, s -> s)); 
table.getColumns().add(column("Address", Person::addressProperty, Address:shortTextForm)); 
+0

感謝James_D的一個非常好的回答,但我無法在我的設置中進行此項工作,我更新了我的原始問題,因此如果您可以請幫助:) – ImRaphael

0

我可能會丟失這裏的要點,但是如果你有適當的人類封裝,你可能還需要使用PropertyValueFactory。如果person_name是Person類中的屬性,那麼

columExample.setCellValueFactory(new PropertyValueFactory<Person, String>("person_name")); 

應該這樣做。

+0

我強烈建議使用lambda表達式代替(遺留) 'PropertyValueFactory'類。 'setCellValueFactory(cellData - > cellData.getValue()。person_nameProperty())'的代碼量相當,但運行速度更快(沒有反射),(最重要的)是編譯時可檢查。編譯器會查找這個方法,所以如果你在方法名中有一個拼寫錯誤,編譯器會捕獲它。使用'PropertyValueFactory'你會得到一個你需要跟蹤的運行時錯誤,這可能會更困難。 –

+0

感謝您的提示! – benji2505