2016-05-11 10 views
1

を変更したとき、私はは、HashMapのは

Binding hashmap with tableview (JavaFX)

この記事に続き、HashMapのからのデータが移入されたテーブルビューを作成して更新するのHashMapとテーブルビューを移植します。

TableViewmap.entrySet()からObservableListと渡すを作成することによって呼び出さHashMapmapからデータを受信するTableViewのコンストラクタにオフObservableList

ObservableListSimpleStringPropertyであるが、基本となるHashMapに変更が加えられた場合、TableViewは更新されません。

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

私は、テーブルビューでのVBoxに追加します。

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(); 
    } 

} 

これはまさに私は、次のボタンを追加した以外Binding hashmap with tableview (JavaFX)からのコードです:

は、ここに私のコードです。

ボタンをクリックすると、TableViewは更新されません。しかし、System.out.println(map)出力のために基礎となるHashMapが実際に変更されたことを確認できます。 TableViewの列ヘッダーをクリックして1つの列でデータを並べ替えると、テーブルデータが再ソートされた後に新しい更新された値が表示されます。

基本マップが変更されたときにテーブルを自動的に更新するにはどうすればよいですか?

これはあなたのtableViewを更新します

+0

私は詳細にこれには見えませんでしたおそらく[ObservableMap](https://docs.oracle.com/javase/8/javafx/api/javafx/collections/FXCollections.html#observableMap-java.util.Map-)を使用したいと考えています。 – jewelsea

+0

これは多分この回答です。http://stackoverflow.com/a/21339428/2855515 – brian

答えて

2

は同じTableViewアイテムやマップのキーを保持して、リスナーとのObservableMapを使用してBindings.valueAtcellValueFactoryを、使用例:

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

ありがとうございます。美しく動作します!興味深いアプローチ、それをストリングのテーブルにして、キーのためのObservableListを別に持っています。私はMapChangeListenerとBindingsも認識していませんでした。とても助かりました。ありがとう! – skrilmps

+0

私はラムダ式の中のバインディングを理解しようとしています。 'ObservableMap 'に対して 'Shade'が' Description'という名前の 'StringProperty'メンバを持つカスタムクラスである(つまり、' getDescription() 'が' String'を返すような同じ機能を使いたいとします。 'descriptionProperty()'は 'StringProperty'を返します)、column2に表示したいと思います。 'column2.setCellValueFactory(cd - > Bindings.valueAt(map、cd.getValue()));行をどのように変更するのですか?どんな洞察力もありがとうございます。 – skrilmps

+0

残念なことに、この種の2レベルバインディングはバインディングAPIでは提供されていないため、マップ値の適切なプロパティにリスナーを追加して独自のプログラムを作成する必要があります(古い値のプロパティから削除する、値が異なるものに変更されたとき)。それは*複雑ではありませんが、コメントセクションは、より詳細な説明を与えるのに適切な場所ではありません。 – fabian

1

マーク、ありがとうございます。

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

ありがとうございます。それはうまくいく。ただし、テーブルに何かを追加するとどうなりますか?例えば、 'map.put(" two "、" 2 ")'を 'map.put(" four "、" Four ")'に変更した場合です。この場合、テーブルは変更を反映するように更新されません。何故なの? – skrilmps

+0

気にしない、なぜ今なぜか分かります。だから、 'table.getColumns()。setAll(column1、column2)'の行を 'table.setItems(FXCollections.observableArrayList(map.entrySet()));に変更することができました。しかし、これはArrayListを再作成しているので、それを使ってテーブルを再充填する。さらに、これを行うと、列見出しをクリックしてすでに確立しているソート順が「破棄」されます。別の方法がありますか? – skrilmps

+0

バージョン> = 8u60の場合は、 'getColumns()。getColumns()。setAll(column1、column2)'を 'refresh()'に置き換えてください。 – fabian