所以我在JavaFx TableView上做了一些挖掘,我找到了一些很好的解决方案来处理简单的情况。
这个article提供了一个很好的解释,说明了如何使用Person对象创建表,还展示了如何使用Map创建表。
这是我的问题,假设我有一个Object Person,其中包含一些简单的成员数据以及一个分配给Grades的地图。
示例:
public class Person {
String firstName;
String lastName;
String age;
Map<Assignment, Grade> map;
}
我想显示如下表格
firstName | lastName | age | Assignment1 | Assignment 2 | Assignment 3 | ...
如果我像这样定义一个表:
private TableView<Student> table;
很容易定义简单的列,如:
private TableColumn<Student, String> firstNameColumn =
firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
但是我应该如何为地图中的每个键定义列?
这是否可能,或者我应该创建另一个处理地图的表?
答案 0 :(得分:4)
您不必使用默认的PropertyValueFactory,您可以编写自己的回调。
import java.util.HashMap;
import java.util.LinkedHashMap;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class AssTable extends Application {
@Override
public void start(Stage primaryStage) {
ObservableList<Student> students = FXCollections.observableArrayList(
new Student("jack"),new Student("john"),new Student("jill"),new Student("jane"));
TableView<Student> studentTable = new TableView(students);
TableColumn<Student, String> firstNameColumn = new TableColumn("name");
firstNameColumn.setCellValueFactory(new PropertyValueFactory("firstName"));
studentTable.getColumns().add(firstNameColumn);
int maxAss = 0;
for (Student student : students)
maxAss = Math.max(maxAss, student.map.size());
Callback<TableColumn.CellDataFeatures<Student, String>, ObservableValue<String>> callBack =
new Callback<TableColumn.CellDataFeatures<Student, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Student, String> param) {
return param.getValue().map.containsKey(
"ass"+Integer.toString((int)param.getTableColumn().getUserData()))
? new SimpleStringProperty(String.format("%.1f",100d*param.getValue().map.get(
"ass"+Integer.toString((int)param.getTableColumn().getUserData()))))
:new SimpleStringProperty("");
}
};
ObservableList<TableColumn<Student, String>> assCols = FXCollections.observableArrayList();
for (int i = 1; i<=maxAss; i++){
TableColumn<Student, String> tmpCol = new TableColumn("ass"+Integer.toString(i));
tmpCol.setUserData(i);
tmpCol.setCellValueFactory(callBack);
assCols.add(tmpCol);
}
studentTable.getColumns().addAll(assCols);
VBox root = new VBox(studentTable);
Scene scene = new Scene(root, 500, 250);
primaryStage.setTitle("Table with map");
primaryStage.setScene(scene);
primaryStage.show();
}
public class Student {
private final StringProperty firstName = new SimpleStringProperty();
public StringProperty firstNameProperty(){return firstName;}
public final HashMap<String, Double> map;
public Student(String fn) {
firstName.set(fn);
map = new LinkedHashMap<>();
for (int i = 1; i <= 10; i++) {
double grade = Math.random();
if (grade > .5) {
map.put("ass" + Integer.toString(i), grade);
}
}
}
}
}
您可以看到它根据分配的数量添加列。此随机样本中也没有人做过ass4。使用此代码和示例,您无法添加#8之类的分配而不添加新列,或者它不会显示。
答案 1 :(得分:1)
我假设每个Map的大小(条目数)在运行时没有变化,或者更好:有一个固定的最大条目。如果是这种情况,TableView
可以使用与标准属性(或Entry
)相同的方式访问每个Property
。
以下是Person
的修改后的类。
public class PersonSimple {
String firstName;
String lastName;
String age;
Map<Integer, Double> map;
public PersonSimple(String fn, String ln, String age, Double gr0, Double gr1, Double gr2)
{
this.firstName = fn;
this.lastName = ln;
this.age = age;
map = new LinkedHashMap<>();
map.put(0, gr0);
map.put(1, gr1);
map.put(2, gr2);
}
public String getFirstName()
{
return firstName;
}
public String getLastName()
{
return firstName;
}
public String getAge()
{
return age;
}
private Double getFromMap(Integer key)
{
Set<Entry<Integer, Double>> s = map.entrySet();
Iterator<Entry<Integer, Double>> iter = s.iterator();
int index = 0;
while(iter.hasNext())
{
Entry<Integer, Double> e = iter.next();
if(index == key.intValue())
{
return e.getValue();
}
index++;
}
return null;
}
public Double getFM0()
{
return getFromMap(0);
}
public Double getFM1()
{
return getFromMap(1);
}
public Double getFM2()
{
return getFromMap(2);
}
}
如您所见,每个PersonSimple
都有一个Map
,必须包含三个条目。现在来了诀窍。对于每个条目,您都定义了一个get方法。请注意如何命名,因为这部分对于与TableView
的交互至关重要。
以下代码显示了如何将这些新方法连接到TableView
。
TableColumn firstNameCol = new TableColumn("First Name");
TableColumn lastNameCol = new TableColumn("Last Name");
TableColumn ageCol = new TableColumn("Age");
TableColumn aCol = new TableColumn("Assignment1");
TableColumn bCol = new TableColumn("Assignment2");
TableColumn cCol = new TableColumn("Assignment3");
table.getColumns().addAll(firstNameCol, lastNameCol, ageCol,aCol,bCol,cCol);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
ageCol.setCellValueFactory(new PropertyValueFactory<Person,String>("age"));
aCol.setCellValueFactory(new PropertyValueFactory<Person,Double>("FM0"));
bCol.setCellValueFactory(new PropertyValueFactory<Person,Double>("FM1"));
cCol.setCellValueFactory(new PropertyValueFactory<Person,Double>("FM2"));
非常重要的是,每个PropertyValueFactor
都会获得一个适合类PersonSimple
中某个get方法的名称。有关详细信息,请参阅the TableView-API。
当然,我的方法并没有解决从动态映射中获取数据的问题,因为据我所知,Java在运行时期间无法向类中添加新方法。但是可能有一个技巧使用reflection-api来规避这种限制。