2015-04-01 63 views
2

我有一个HashMap。我想在ListView中显示这些键。JavaFX Listview观察地图

问题是,ListView.setItems()想要一个ObservableList,而我所拥有的只是一个keySet()。

我该如何获得一个ListView来观察我的Map中的键,而不是像维护两个匹配的数据结构那样做一些笨重的事情?

回答

4

我知道这不是你想要的答案,但是......我的建议是维护两个数据结构。

示例应用程序(synching数据结构)

使用右侧的UI添加项目到地图,并作为extension -> mimeType地图变化的钥匙,你会看到在ListView显示的键列表在左边自动更新。

该解决方案侦听对包装extension -> mimetype映射的ObservableMap的更改,并且当映射中的某个键发生更改时,将相关更新应用于支持ListView的ObservableList。

在下面的示例屏幕截图中,当用户按下添加按钮时,png将被添加到左侧列表中。

mapobserver

import javafx.application.Application; 
import javafx.collections.*; 
import javafx.geometry.Insets; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.layout.*; 
import javafx.stage.Stage; 

import java.util.Arrays; 
import java.util.Map; 
import java.util.stream.Collectors; 

public class ObservableMapTest extends Application { 
    // map initializer based on http://stackoverflow.com/a/25829097/1155209 
    private static final Map<String, String> extensionToMimeMap = 
      Arrays.stream(new String[][]{ 
        {"txt", "text/plain"}, 
        {"html", "text/html"}, 
        {"js", "application/javascript"}, 
        {"css", "text/css"} 
      }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1])); 

    @Override 
    public void start(Stage stage) throws Exception { 
     // create an observable wrapper for our map data. 
     final ObservableMap<String, String> observableExtensionToMimeMap = FXCollections.observableMap(
       extensionToMimeMap 
     ); 

     // create an ListView based on key items in the map. 
     ListView<String> extensionListView = new ListView<>(); 
     extensionListView.getItems().setAll(extensionToMimeMap.keySet()); 
     extensionListView.setPrefWidth(100); 

     // have the ListView observe the underlying map and modify its items if the key set changes. 
     observableExtensionToMimeMap.addListener((MapChangeListener<String, String>) change -> { 
      extensionListView.getItems().removeAll(change.getKey()); 
      if (change.wasAdded()) { 
       extensionListView.getItems().add(change.getKey()); 
      } 
     }); 

     // layout the app. 
     Pane layout = new HBox(
       extensionListView, 
       createAddExtensionPane(
         observableExtensionToMimeMap 
       ) 
     ); 
     layout.setPrefHeight(150); 

     // display the app. 
     stage.setScene(new Scene(layout)); 
     stage.show(); 
    } 

    /** Helper factory function to create a UI for adding an element to an map. */ 
    private GridPane createAddExtensionPane(Map<String, String> map) { 
     GridPane addExtensionPane = new GridPane(); 

     addExtensionPane.add(new Label("Extension:"), 0, 0); 
     TextField extensionField = new TextField(); 
     addExtensionPane.add(extensionField, 1, 0); 

     addExtensionPane.add(new Label("Mime Type:"), 0, 1); 
     TextField mimeTypeField = new TextField(); 
     addExtensionPane.add(mimeTypeField, 1, 1); 

     Button addButton = new Button("Add"); 
     addButton.setOnAction(event -> 
       map.put(
         extensionField.getText(), 
         mimeTypeField.getText() 
       ) 
     ); 
     addExtensionPane.add(addButton, 1, 2); 
     addExtensionPane.setPadding(new Insets(10)); 
     addExtensionPane.setHgap(5); 
     addExtensionPane.setVgap(10); 

     return addExtensionPane; 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

一个可能的替代实施方案,其并不数据复制到一个ObservableList将实现ObservableList接口和在实施的方法直接引用可观察地图的密钥数据。这种方法的实施非常复杂,不值得追求(IMO)。

+0

这是一个很好的,详细的答案。谢谢! – Steve 2015-04-02 02:44:45