【问题标题】:JavaFX TableView Objects with Maps带有地图的 JavaFX TableView 对象
【发布时间】:2014-02-07 22:41:22
【问题描述】:

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

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

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

【问题讨论】:

    标签: java javafx tableview


    【解决方案1】:

    您不必使用默认的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 这样的赋值,否则它不会显示。

    【讨论】:

    • 太棒了,很棒的解决方案!
    【解决方案2】:

    我假设每个 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);
    }
    

    }

    如您所见,每个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 中不可能在运行时向类添加新方法。但是可能有一个使用反射 API 的技巧来规避这个限制。

    【讨论】:

    • 嘿伙计,固定地图的绝佳解决方案!不幸的是,这张地图可能会在运行时发生变化,这导致我开始研究反射作为一种可能的解决方案。似乎 TableView 在它可以实现的方面有些限制......我想知道是否有某种特殊的方式来设置表格来绕过这种使用 Default PropertyValueFactory 的方式。
    猜你喜欢
    • 2017-05-24
    • 2014-06-27
    • 1970-01-01
    • 2015-11-05
    • 2014-01-17
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2021-08-05
    相关资源
    最近更新 更多