2016-05-11 14 views
1

ändert ich diesen Beitrageine Tableview mit einem HashMap Bestücken, die aktualisieren, wenn HashMap

Binding hashmap with tableview (JavaFX)

und schuf eine Tableview gefolgt sind, die durch Daten aus einer HashMap bevölkert ist.

Die TableView erhält seine Daten von einem HashMap genannt map durch eine ObservableList von map.entrySet() Erstellung und Übergabe, dass ObservableList aus nach TableView ‚s Konstruktor. (Code ist unten)

Obwohl es ein ObservableList mit SimpleStringProperty s ist, wird das TableView nicht aktualisiert, wenn Änderungen an der zugrunde liegenden HashMap vorgenommen werden.

Hier ist mein Code:

public class MapTableView extends Application { 

    @Override 
    public void start(Stage stage) throws Exception { 
     try { 
      // sample data 
      Map<String, String> map = new HashMap<>(); 
      map.put("one", "One"); 
      map.put("two", "Two"); 
      map.put("three", "Three"); 


      // use fully detailed type for Map.Entry<String, String> 
      TableColumn<Map.Entry<String, String>, String> column1 = new TableColumn<>("Key"); 
      column1.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() { 

       @Override 
       public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) { 
        // this callback returns property for just one cell, you can't use a loop here 
        // for first column we use key 
        return new SimpleStringProperty(p.getValue().getKey()); 
       } 
      }); 

      TableColumn<Map.Entry<String, String>, String> column2 = new TableColumn<>("Value"); 
      column2.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() { 

       @Override 
       public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) { 
        // for second column we use value 
        return new SimpleStringProperty(p.getValue().getValue()); 
       } 
      }); 

      ObservableList<Map.Entry<String, String>> items = FXCollections.observableArrayList(map.entrySet()); 
      final TableView<Map.Entry<String,String>> table = new TableView<>(items); 

      table.getColumns().setAll(column1, column2); 

      Button changeButton = new Button("Change"); 
      changeButton.setOnAction((ActionEvent e) -> { 
       map.put("two", "2"); 
       System.out.println(map); 
      }); 
      VBox vBox = new VBox(8); 
      vBox.getChildren().addAll(table, changeButton); 

      Scene scene = new Scene(vBox, 400, 400); 
      stage.setScene(scene); 
      stage.show(); 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     launch(); 
    } 

} 

Dies ist genau der Code von Binding hashmap with tableview (JavaFX) außer ich die folgende Schaltfläche hinzugefügt haben:

 Button changeButton = new Button("Change"); 
     changeButton.setOnAction((ActionEvent e) -> { 
      map.put("two", "2"); 
      System.out.println(map); 
     }); 

, die ich dann mit dem Tableview zu einem VBox hinzuzufügen.

Wenn ich auf die Schaltfläche klicke, wird TableView nicht aktualisiert. Ich kann jedoch überprüfen, dass der zugrunde liegende HashMap tatsächlich wegen der System.out.println(map) Ausgabe geändert hat. Wenn ich auf eine Spaltenüberschrift in TableView klicke, um die Daten nach einer Spalte zu sortieren, wird der neue aktualisierte Wert angezeigt, nachdem die Tabellendaten neu sortiert werden.

Wie kann ich die Tabelle automatisch aktualisieren, wenn die zugrunde liegende Karte geändert wird?

Danke,

Mark

+0

ich sah nicht so aus in diesen im Detail Vielleicht möchten Sie aber eine [ObservableMap] (https://docs.oracle.com/javase/8/javafx/api/javafx/collections/FXCollections.html#observableMap-java.util.Map-) verwenden. – jewelsea

+0

Vielleicht diese Antwort http://stackoverflow.com/a/21339428/2855515 – brian

Antwort

2

ein ObservableMap mit einem Hörer verwenden, die die TableView Elemente und die Schlüssel der Karte, um die cellValueFactory mit Bindings.valueAt, zum Beispiel gleich und verwenden halten:

ObservableMap<String, String> map = FXCollections.observableHashMap(); 

ObservableList<String> keys = FXCollections.observableArrayList(); 

map.addListener((MapChangeListener.Change<? extends String, ? extends String> change) -> { 
    boolean removed = change.wasRemoved(); 
    if (removed != change.wasAdded()) { 
     // no put for existing key 
     if (removed) { 
      keys.remove(change.getKey()); 
     } else { 
      keys.add(change.getKey()); 
     } 
    } 
}); 

map.put("one", "One"); 
map.put("two", "Two"); 
map.put("three", "Three"); 

final TableView<String> table = new TableView<>(keys); 

TableColumn<String, String> column1 = new TableColumn<>("Key"); 
// display item value (= constant) 
column1.setCellValueFactory(cd -> Bindings.createStringBinding(() -> cd.getValue())); 

TableColumn<String, String> column2 = new TableColumn<>("Value"); 
column2.setCellValueFactory(cd -> Bindings.valueAt(map, cd.getValue())); 

table.getColumns().setAll(column1, column2); 
+0

Vielen Dank. Funktioniert wunderbar!Interessanter Ansatz, macht es zu einer Tabelle von Strings und hat eine separate ObservableList für die Schlüssel. Ich kannte auch MapChangeListener und Bindings nicht. Sehr hilfreich, danke! – skrilmps

+0

Ich versuche die Bindungen innerhalb der Lambda-Ausdrücke zu verstehen. Angenommen, ich möchte nun die gleiche Funktionalität für eine ObservableMap 'haben, wobei' Shade' eine benutzerdefinierte Klasse mit einem 'StringProperty' Member namens' Description' ist (dh 'getDescription()' gibt eine 'String' zurück und 'descriptionProperty()' gibt eine 'StringProperty' zurück, die ich in Spalte2 anzeigen möchte. Wie würde ich die Zeile 'column2.setCellValueFactory (cd -> Bindings.valueAt (map, cd.getValue())) ändern;'? Danke für jede Einsicht. – skrilmps

+0

Leider wird diese Art der 2-Level-Bindung von der Bindings-API nicht bereitgestellt, daher müssen Sie sie selbst programmieren, indem Sie der entsprechenden Eigenschaft des Map-Werts einen Listener hinzufügen (und ihn aus der Eigenschaft des alten Werts entfernen). wenn der Wert auf den anderen Wert geändert wird). Es ist nicht kompliziert, aber der Kommentarbereich ist nicht der geeignete Ort, um eine detailliertere Beschreibung zu geben. – fabian

1

Ist dies Ihre Tableview aktualisiert.

changeButton.setOnAction(e -> { 
       map.put("two", "2"); 
       table.getColumns().setAll(column1, column2); 
       System.out.println(map); 
}); 
+0

Danke. Das funktioniert. Was aber, wenn ich etwas zum Tisch hinzufüge? Zum Beispiel, wenn ich die Zeile 'map.put (" zwei "," 2 ")' zu 'map.put (" vier "," Vier ")' 'ändere. In diesem Fall wird die Tabelle nicht aktualisiert, um die Änderung widerzuspiegeln. Warum nicht? – skrilmps

+0

Nevermind, ich verstehe warum jetzt. Also könnte ich die Zeile 'table.getColumns(). SetAll (column1, column2)' in 'table.setItems (FXCollections.observableArrayList (map.entrySet())) ändern;' Aber das scheint unelegant, weil ich eine ArrayList und Repopulation der Tabelle damit. Darüber hinaus "zerstört" dies jede Sortierreihenfolge, die der Benutzer möglicherweise bereits durch Klicken auf eine Spaltenüberschrift festgelegt hat. Gibt es eine andere Art und Weise? – skrilmps

+0

Für Versionen> = 8u60 sollten Sie wahrscheinlich die 'getColumns() ersetzen. GetColumns(). SetAll (column1, column2)' with 'refresh()' – fabian