列格式化

问题描述:

可重复使用的方法,我在下面的方法我的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

在你举的情况下,你应该使用单元格值工厂:

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

我可能会丢失这里的要点,但是如果你有适当的人类封装,你可能还需要使用PropertyValueFactory。如果person_name是Person类中的属性,那么

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

应该这样做。

+0

我强烈建议使用lambda表达式代替(遗留) 'PropertyValueFactory'类。 'setCellValueFactory(cellData - > cellData.getValue()。person_nameProperty())'的代码量相当,但运行速度更快(没有反射),(最重要的)是编译时可检查。编译器会查找这个方法,所以如果你在方法名中有一个拼写错误,编译器会捕获它。使用'PropertyValueFactory'你会得到一个你需要跟踪的运行时错误,这可能会更困难。 –

+0

感谢您的提示! – benji2505