为了便于在JavaFX应用程序中创建带有列的TableView,我想到创建一个注释来指示模型类中的字段,我想创建一个与其关联的单元格值类型的TableColumn。这是我写的注释:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AsTableColumn {
String text();
int index();
}
样本模型类Person就像这样:
public class Person {
@AsTableColumn(text="Name", index=0)
private String name;
@AsTableColumn(text="Age", index=0)
private int age;
// setters and getters
}
在使用JavaFX中的列创建TableView的本机方式中,我必须编写如下代码:
TableView<Person> table = new TableView<>();
TableColumn<Person, String> nameCol = new TableColumn<Person, String>("Name");
TableColumn<Person, Integer> ageCol = new TableColumn<Person, Integer>("Age");
nameCol.setCellValueFactory(cellData -> new SimpleObjectProperty<String>(cellData.getValue().getName()));
ageCol.setCellValueFactory(cellData -> new SimpleObjectProperty<Integer>(cellData.getValue().getAge()));
我有几个包含很多列的表,所以编写很多这样的代码很烦人,我认为应该有一种简化它的方法。
当我编写注释处理器时,我遇到了一个创建TableColumn<T,S>
实例的问题,其中T在方法签名中被参数化,但S在运行时被动态检测。
import java.lang.reflect.Field;
import javafx.scene.control.TableColumn;
public class AnnotationProcessor {
/**
* Process the model class.
*
* For the fields declared which have annotation AsTableColumn, create a TableColumn<T,S>
* instance for it with the detected field type.
* @param clas
*/
public static <T> List<TableColumn<T, ? extends Object>> process(Class<T> clas) {
for(Field field : clas.getDeclaredFields()) {
if(field.isAnnotationPresent(AsTableColumn.class)) {
AsTableColumn anno = field.getAnnotation(AsTableColumn.class);
Class<?> type = field.getType();
TableColumn<T, /* how to specify the field type here???*/> col = new TableColumn<>();
}
}
}
}
答案 0 :(得分:3)
您可以创建方法
private static <T,S> TableColumn<T,S> createTableColumn(Class<S> type, Field field, String text) {
TableColumn<T, S> col = new TableColumn<T,S>(text);
col.setCellValueFactory(cellData -> {
try {
boolean wasAccessible = field.isAccessible() ;
field.setAccessible(true);
@SuppressWarnings("unchecked")
SimpleObjectProperty<S> property = new SimpleObjectProperty<S>((S)(field.get(cellData.getValue())));
field.setAccessible(wasAccessible);
return property;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return col ;
}
然后再做
TableColumn<T, ?> col = createTableColumn(type, field, anno.text());
但是,传递这种类型并不是很清楚。我想你可以做到:
AsTableColumn anno = field.getAnnotation(AsTableColumn.class);
TableColumn<T, Object> col = new TableColumn<>(anno.text());
col.setCellValueFactory(cellData -> {
try {
boolean wasAccessible = field.isAccessible() ;
field.setAccessible(true);
SimpleObjectProperty<Object> property = new SimpleObjectProperty<>(field.get(cellData.getValue()));
field.setAccessible(wasAccessible);
return property;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
本质上是@Thomas发布(并删除)的解决方案。
FWIW,我通常只使用静态实用程序方法来创建表列:
private static <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
然后你可以做
table.getColumns().add(column("Name", Person::nameProperty));
table.getColumns().add(column("Age", Person::ageProperty));
如果你不使用JavaFX属性,那么很容易使其适应private static <S,T> TableColumn<S,T> column(String text, Function<S,T> property);
,但我认为你必须为原始类型制作特殊情况。