2016-08-12 46 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,它是它的孩子。 当出现嵌套值时,以前需要递归发生。 我目前正在尝试编写一个类似自己的解决方案。如果我成功,我会提交。 –