2016-08-12 76 views
2

我有一個嵌套的HashMap它看起來像這樣:Java HashMap到JavaFX Treeview和Back?

Map<String,Object> myMap = new HashMap<String,Object>(); 

myMap嵌套,像:

String key => String val 
String key => String val 
String key => Map<String,Object> val 

然後該值可能包含其他類似Map<String,Object>。 我不指望它嵌套超過3個級別。 葉子或最後的值始終爲String

現在我試圖編寫一個在JavaFX GUI中編輯此HashMap的方法。

從我已經學會爲止, 最好的辦法似乎是製作可編輯的JavaFX TreeView 並以某種方式翻譯MapTreeView和背部。

到目前爲止,我想

TreeMap<String, Object> treeMap = new TreeMap<String, Object>(); 
treeMap.putAll(myMap); 

然後以某種方式轉換是TreeMap到一個JavaFX TreeView。 但我不知道如何繼續。 另一個令人頭疼的問題是,在用戶編輯之後,我需要將它全部翻譯回HashMap,例如原始myMap。雖然排序/序列不是必需的。

回答

1

創建一個合適的類來表示地圖條目。由於您需要修改Map,因此您需要在該類中存儲密鑰和Map

如果使用TextFieldTreeCell作爲細胞類型,StringConverter可以用來修改一個編輯的源數據結構:

private static TreeItem<MapItem> createTree(Map<String, Object> map) { 
    TreeItem<MapItem> result = new TreeItem<>(); 

    for (Map.Entry<String, Object> entry : map.entrySet()) { 
     result.getChildren().add(createTree(map, entry)); 
    } 

    return result; 
} 

private static TreeItem<MapItem> createTree(Map<String, Object> map, Map.Entry<String, Object> entry) { 
    MapItem mi = new MapItem(map, entry.getKey()); 
    TreeItem<MapItem> result = new TreeItem<>(mi); 

    Object value = entry.getValue(); 

    if (value instanceof Map) { 
     Map<String, Object> vMap = (Map<String, Object>)value; 

     // recursive creation of subtrees for map entries 
     for (Map.Entry<String, Object> e : vMap.entrySet()) { 
      result.getChildren().add(createTree(vMap, e)); 
     } 
    } else { 
     result.getChildren().add(new TreeItem<>(new MapItem(null, value.toString()))); 
    } 

    return result; 
} 

private static class MapItem { 

    private final Map<String, Object> map; 
    private final String value; 

    public MapItem(Map<String, Object> map, String value) { 
     this.map = map; 
     this.value = value; 
    } 
} 

private static class Converter extends StringConverter<MapItem> { 

    private final TreeCell<MapItem> cell; 

    public Converter(TreeCell<MapItem> cell) { 
     this.cell = cell; 
    } 

    @Override 
    public String toString(MapItem object) { 
     return object == null ? null : object.value; 
    } 

    @Override 
    public MapItem fromString(String string) { 
     MapItem mi = cell.getItem(); 

     if (mi != null) { 
      TreeItem<MapItem> item = cell.getTreeItem(); 
      if (item.isLeaf()) { 
       MapItem parentItem = item.getParent().getValue(); 

       // modify value in parent map 
       parentItem.map.put(parentItem.value, string); 
       mi = new MapItem(mi.map, string); 
      } else if (!mi.map.containsKey(string)) { 
       // change key of mapping, if there is no mapping for the new key 
       mi.map.put(string, mi.map.remove(mi.value)); 
       mi = new MapItem(mi.map, string); 
      } 
     } 

     return mi; 
    } 

} 

@Override 
public void start(Stage primaryStage) { 
    Map<String, Object> map = new HashMap<>(); 

    map.put("a", "b"); 

    Map<String, Object> inner = new HashMap<>(); 
    map.put("c", inner); 
    inner.put("d", "e"); 

    Map<String, Object> inner2 = new HashMap<>(); 
    inner.put("f", inner2); 
    inner2.put("g", "h"); 
    inner2.put("i", "j"); 

    TreeView<MapItem> treeView = new TreeView<>(createTree(map)); 
    treeView.setEditable(true); 
    treeView.setShowRoot(false); 

    treeView.setCellFactory(t -> { 
     TextFieldTreeCell<MapItem> cell = new TextFieldTreeCell<>(); 
     cell.setConverter(new Converter(cell)); 
     return cell; 
    }); 

    Button btn = new Button("Print Map"); 
    btn.setOnAction((ActionEvent event) -> { 
     System.out.println(map); 
    }); 

    VBox root = new VBox(10, btn, treeView); 

    Scene scene = new Scene(root); 

    primaryStage.setScene(scene); 
    primaryStage.show(); 
} 
+0

這幾乎就是我如何解決它, 登錄以發佈它,並在這裏。 –

1

沒有內置的方法,將轉換:

  1. 要麼Map直接TreeView
  2. TreeMapTreeView

此外,如果你看到的Map<?,?>結構,我們可以看到它是key, value對的組合,而TreeView<?>僅包含值,如Collection接口。所以無法在TreeView中插入key,value對。但是,只能插入Map中的值。

你能做的最好的是定義一個新的數據結構是這樣的:

public class YourCustomDataStructure extends TreeItem<String> { 
    ... 

    /* 
    Now you can define methods that will convert `List`s directly to `YourCustomDataStructure`. 
    You can even define method to convert `Map` values to `YourCustomDataStructure`. 
    */ 

    public boolean addAll(Map map) { 
     //your implementation to convert map to TreeItem 
    } 

    public boolean addAll(List list) { 
     //your implementation to convert list to TreeItem 
    } 


} 

現在,使用前一步驟addAll()方法轉換您的listmapYourCustomDataStructure

List<Object> list = getListFromSomewhere(); 

YourCustomDataStructure<String> customList = new YourCustomDataStructure<String>("customList Node"); 
customList.getChildren().addAll(list); 

現在既然YourCustomDataStructure擴展TreeItem所以它的對象可以直接傳遞給TreeView的構造,他們將被自動轉換爲TreeView

TreeView<String> treeView = new TreeView<String>(customList); 

P.S.:我知道定義新的數據結構,所有的方法都需要在初始階段進行大量的工作,但是一旦定義了這些方法,那麼將TreeView轉換爲Map變得非常容易,反之亦然。

+0

謝謝回答,但這種方法的問題是,我原來的Hashmap嵌套。所以它不可能將嵌套的Hashmap轉換爲TreeItem。相反,關鍵需要成爲根TreeItem。並且該鍵下的值需要是另一個TreeItem,它是它的孩子。 當出現嵌套值時,以前需要遞歸發生。 我目前正在嘗試編寫一個類似自己的解決方案。如果我成功,我會提交。 –