2013-06-05 17 views
7

Volevo sapere se è possibile utilizzare uno? Io uso ObservableMap invece di ObservableList perché ho bisogno di aggiungere ed eliminare spesso, quindi ho bisogno di ridurre al minimo il costo.Popola TableView con ObservableMap JavaFX

My hashMap utilizza un BigInteger come campo chiave e un tipo con molte proprietà come campo valore. Nella mia tabellaView voglio solo visualizzare i valori con una colonna per proprietà. Spero che sia chiaro

Grazie

risposta

3

Ho provato a fare questo. Immagino che il post sia vecchio ma non vedo alcuna risposta da nessuna parte sulla rete. Gli esempi utilizzano la chiave della mappa per le colonne e quindi un elenco di mappe per ogni riga. Mi piacerebbe vedere le righe come chiavi e i loro valori associati. È un lungo esempio

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

e al codice LineItem Classe

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"; 
    } 
} 

potete vedere dopo la modifica dei dati e facendo clic su che cambiamenti nella lista si riflettono nella mappa. Devo ancora controllare l'altro modo e gestire gli inserimenti e le eliminazioni, ma non dovrebbe essere troppo difficile.

+0

Questo sarebbe molto più bello se ObservableMap # entrySet fosse covarmente sovraccarico per restituire un ObservableSet piuttosto che un set regolare. - anche se questo non è vero, perché una tableview vuole una lista osservabile, e non esiste un modo semplice per passare da una serie osservabile a una lista osservabile. – Groostav

0

Ho impacchettato i listener di Table Table in una sottoclasse di 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); 
    } 

} 

Sembra funzionare finora. Creo con il seguente codice:

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

È anche possibile modificare i tasti.

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

È possibile vedere che ho aggiunto un metodo per rimuovere e aggiungere nuovamente i listener di mappe. Per aggiungere e rimuovere voci da 100k occorrono .65 secondi senza ascoltatori e 5.2 secondi con loro.

Ecco il tutto in un unico file su pastebin. http://pastebin.com/NmdTURFt