2014-02-07 56 views
2

所以我在JavaFx TableView上做了一些探索,我发现了一些很好的解决方案,以简单的情况。JavaFX带有地图的TableView对象

这个article提供了一个很好的解释,说明如何用Person对象创建一个表格,并且演示了如何使用Map创建一个表格。

这是我的问题,比方说,我有一个对象人员,其中包含一些简单的成员数据,也是一个地图的作业分级地图。

实施例:

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")); 

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

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

回答

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可以按照与标准属性(或Property)相同的方式访问每个Entry。 这里是一个修改后的类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); 
} 

}

正如你可以看到,每一个PersonSimpleMap必须持有三个条目。现在来诀窍。对于每个条目,您都定义了一个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一开始的方法之一的名称。有关详细信息,请参见the TableView-API

当然,我的方法并没有解决从动态映射中获取数据的问题,因为据我所知,在运行时在Java中添加新方法是不可能的。但是使用reflection-api来绕过这个限制可能会有一些技巧。

+0

嘿人,一个固定的地图很好的解决方案!不幸的是,这张地图可能在运行时发生变化,这导致我开始将反射看作一种可能的解决方案。看起来TableView在它的实现方面有一定的局限性......我想知道是否有某种特殊的方式来设置表格来绕过这种使用Default PropertyValueFactory的方式。 –