2014-02-07 4 views
2

Итак, я сделал кое-какие работы в JavaFx TableView, и я нашел несколько хороших решений для простых ситуаций.JavaFX TableView Объекты с картами

Это article дает хорошее объяснение того, как создать таблицу с объектом Person, а также показано, как создать таблицу с использованием карты.

Вот моя проблема, допустим, у меня есть объект-объект, который содержит некоторые простые данные элемента, а также карту присвоений классам.

Пример:

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

Awesome, отличное решение! –

1

Я предположил, что размер (# записей) каждой Карты не изменяется во время выполнения или лучше: существует фиксированный максимум записей. Если это так, то 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 получает имя, которое соответствует одному из GET-методов в классе PersonSimple. См. the TableView-API для получения дополнительной информации.

Конечно, мой подход не решает проблему с получением данных с динамических карт, поскольку, насколько я знаю, в Java невозможно добавлять новые методы в класс во время выполнения. Но может быть трюк, использующий отражение-api, чтобы обойти это ограничение.

+0

Эй, человек, отличное решение для фиксированной карты! К сожалению, эта карта может измениться во время выполнения, что заставляет меня начать рассматривать отражение как возможное решение. Кажется, TableView несколько ограничен в том, чего он может достичь ... Мне было интересно, есть ли какой-то особый способ настроить таблицу, чтобы обойти этот способ использования Default PropertyValueFactory. –

Смежные вопросы