带有地图的JavaFX TableView对象

时间:2014-02-07 22:41:23

标签: java javafx tableview

所以我在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"));

但是我应该如何为地图中的每个键定义列?

这是否可能,或者我应该创建另一个处理地图的表?

2 个答案:

答案 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);
                }
            }
        }
    }
}

Screen Shot

您可以看到它根据分配的数量添加列。此随机样本中也没有人做过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来规避这种限制。