2013-06-05 157 views
7

我想知道是否可以使用ObservableMap来填充TableView? 我使用ObservableMap而不是ObservableList,因为我需要经常添加和删除,所以我需要最小化成本。使用ObservableMap填充TableView JavaFX

我的hashMap使用BigInteger作为关键字段和一个具有许多属性值类型的类型。 在我的tableView中,我只想显示每个属性的列的值。我希望这是明确的

谢谢

回答

3

我一直在试图做到这一点。我猜这个帖子很旧,但我在网上的任何地方都没有看到任何答案。这些示例对列使用map键,然后对每一行使用地图列表。我希望将行看作键和关联的值。这是一个很长的例子。

package tablemap; 

import static java.lang.Math.random; 
import java.util.Map; 
import java.util.TreeMap; 
import javafx.application.Application; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.event.ActionEvent; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableColumn.CellEditEvent; 
import javafx.scene.control.TableView; 
import javafx.scene.control.cell.TextFieldTableCell; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class TableMap extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     VBox root = new VBox(); 
     Map<String,LineItem> mapData = new TreeMap<>(); 
     for (int i = 0; i < 3; i++) 
      mapData.put(String.valueOf(random()), new LineItem(String.valueOf(i),"i")); 

     ObservableList<Map.Entry<String,LineItem>> listData = 
       FXCollections.observableArrayList(mapData.entrySet()); 
     TableView<Map.Entry<String,LineItem>> tv = new TableView(listData); 

     TableColumn<Map.Entry<String,LineItem>,String> keyCol = new TableColumn("Key"); 
     keyCol.setCellValueFactory(
      (TableColumn.CellDataFeatures<Map.Entry<String,LineItem>, String> p) -> 
       new SimpleStringProperty(p.getValue().getKey())); 

     TableColumn<Map.Entry<String,LineItem>,String> lineNoCol = new TableColumn("Line No"); 
     lineNoCol.setCellValueFactory(
      (TableColumn.CellDataFeatures<Map.Entry<String,LineItem>, String> p) -> 
       new SimpleStringProperty(p.getValue().getValue().getLineNo())); 

     TableColumn<Map.Entry<String,LineItem>,String> descCol = new TableColumn("Desc"); 
     descCol.setCellValueFactory(
      (TableColumn.CellDataFeatures<Map.Entry<String,LineItem>, String> p) -> 
       new SimpleStringProperty(p.getValue().getValue().getDesc())); 

     descCol.setCellFactory(TextFieldTableCell.forTableColumn()); 

     descCol.setOnEditCommit((CellEditEvent<Map.Entry<String,LineItem>, String> t) -> { 
      t.getTableView().getItems().get(t.getTablePosition().getRow()) 
        .getValue().setDesc(t.getNewValue()); 
     }); 

     tv.getColumns().addAll(keyCol,lineNoCol, descCol); 
     tv.setEditable(true); 
     tv.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); 

     Button btnOut = new Button("out"); 
     btnOut.setOnAction(new EventHandler<ActionEvent>() { 
      @Override 
      public void handle(ActionEvent t) { 
       for (Map.Entry<String,LineItem> me : mapData.entrySet()){ 
        System.out.println("key "+me.getKey()+" entry "+me.getValue().toCSVString()); 
       } 
       for (Map.Entry<String,LineItem> me : listData){ 
        System.out.println("key "+me.getKey()+" entry "+me.getValue().toCSVString()); 
       } 
      } 
     }); 

     root.getChildren().addAll(tv,btnOut); 
     Scene scene = new Scene(root, 300, 200); 

     primaryStage.setTitle("Map Table Test"); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 
} 

而且LineItem类代码

package tablemap; 

import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

/* LineItem class */ 

public class LineItem { 
    private final StringProperty lineNo = new SimpleStringProperty(); 
    private final StringProperty desc = new SimpleStringProperty(); 

    public LineItem(String ln, String dsc) { 
     lineNo.set(ln); desc.set(dsc); 
    } 

    public String getLineNo() {return (lineNo.getValue() != null) ?lineNo.get():"";} 
    public void setLineNo(String lineNo) {this.lineNo.set(lineNo);} 
    public StringProperty lineNoProperty() {return lineNo;} 

    public String getDesc() {return (desc.getValue() != null) ?desc.get():"";} 
    public void setDesc(String desc) {this.desc.set(desc);} 
    public StringProperty descProperty() {return desc;} 

    public String toCSVString(){ 
     return lineNo.getValueSafe()+","+ 
       desc.getValueSafe()+"\n"; 
    } 
} 

你可以看到编辑的数据,然后点击了该列表中的变化反映在地图后。我仍然需要检查其他方式并处理插入和删除操作,但这不应该很难。

+0

如果ObservableMap#entrySet被协变重载以返回一个ObservableSet而不是一个常规集,这将会更好。 - 尽管这不是真的,因为tableview需要一个可观察的列表,并且从可观察集到可观察列表都没有简单的方法。 – Groostav

0

我把我的Map Table侦听器打包在TableView的子类中。

package tablemap; 

import java.util.AbstractMap; 
import java.util.Map; 
import javafx.collections.FXCollections; 
import javafx.collections.ListChangeListener; 
import javafx.collections.MapChangeListener; 
import javafx.collections.ObservableList; 
import javafx.collections.ObservableMap; 
import javafx.scene.control.TableView; 

public class MapTableView<K,V> extends TableView<Map.Entry<K,V>>{ 
    private final ObservableList<Map.Entry<K,V>> obsList; 
    private final ObservableMap<K,V> map; 
    private final MapChangeListener<K,V> mapChange; 
    private final ListChangeListener<Map.Entry<K,V>> listChange; 

    public MapTableView(ObservableMap<K,V> map) { 
     this.map = map; 
     obsList = FXCollections.observableArrayList(map.entrySet()); 
     setItems(obsList); 

     mapChange = new MapChangeListener<K, V>() { 
      @Override 
      public void onChanged(MapChangeListener.Change<? extends K, ? extends V> change) { 
       obsList.removeListener(listChange); 
       if (change.wasAdded()) 
        obsList.add(new AbstractMap.SimpleEntry(change.getKey(),change.getValueAdded())); 
       if (change.wasRemoved()){ 
        //obsList.remove(new AbstractMap.SimpleEntry(change.getKey(),change.getValueRemoved())); 
        //^doesn't work always, use loop instead 
        for (Map.Entry<K,V> me : obsList){ 
         if (me.getKey().equals(change.getKey())){ 
          obsList.remove(me); 
          break; 
         } 
        } 
       } 
       obsList.addListener(listChange); 
      } 
     }; 

     listChange = (ListChangeListener.Change<? extends Map.Entry<K, V>> change) -> { 
      map.removeListener(mapChange); 
      while (change.next()){ 
       //maybe check for uniqueness here 
       if (change.wasAdded()) for (Map.Entry<K, V> me: change.getAddedSubList()) 
        map.put(me.getKey(),me.getValue()); 
       if (change.wasRemoved()) for (Map.Entry<K, V> me: change.getRemoved()) 
        map.remove(me.getKey()); 
      } 
      map.addListener(mapChange); 
     }; 

     map.addListener(mapChange); 
     obsList.addListener(listChange); 
    } 

    //adding to list should be unique 
    public void addUnique(K key, V value){ 
     boolean isFound = false; 
     //if a duplicate key just change the value 
     for (Map.Entry<K,V> me : getItems()){ 
      if (me.getKey().equals(key)){ 
       isFound = true; 
       me.setValue(value); 
       break;//only first match 
      } 
     } 
     if (!isFound) // add new entry 
      getItems().add(new AbstractMap.SimpleEntry<>(key,value)); 
    } 

    //for doing lenghty map operations 
    public void removeMapListener(){ 
     map.removeListener(mapChange); 
    } 

    //for resyncing list to map after many changes 
    public void resetMapListener(){ 
     obsList.removeListener(listChange); 
      obsList.clear(); 
      obsList.addAll(map.entrySet()); 
     obsList.addListener(listChange); 
     map.addListener(mapChange); 
    } 

} 

它似乎工作到目前为止。我用以下代码创建:

final ObservableMap<String, LineItem> obsMap = FXCollections.observableHashMap(); 
final MapTableView<String,LineItem> mtv = new MapTableView(obsMap); 

您甚至可以编辑密钥。

final TableColumn<Map.Entry<String,LineItem>,String> keyCol = new TableColumn("Key"); 
keyCol.setCellValueFactory(
    (TableColumn.CellDataFeatures<Map.Entry<String,LineItem>, String> p) -> 
     new SimpleStringProperty(p.getValue().getKey())); 
keyCol.setCellFactory(TextFieldTableCell.forTableColumn()); 
keyCol.setOnEditCommit((CellEditEvent<Map.Entry<String,LineItem>, String> t) -> { 
    final String oldKey = t.getOldValue(); 
    final LineItem oldLineItem = obsMap.get(oldKey); 
    obsMap.remove(oldKey);//should remove from list but maybe doesn't always 
    obsMap.put(t.getNewValue(),oldLineItem); 
}); 

你可以看到我添加了一个方法来删除并重新添加地图监听器。要添加和删除100k条目需要0.65秒(不包括听众)和5.2秒。

下面是pastebin上的一个文件的全部内容。 http://pastebin.com/NmdTURFt