Binding hashmap with tableview (JavaFX) - javafx-2

I want to display HashMap contents in a JavaFX Tableview. Please find below the code I used to set the HashMap contents into the table columns. The problem I'm having is that it's displaying only one row. The for loop is iterating only 5 times: each time it is picking up the first value of the HashMap.
If I ignore the return SimpleObjectProperty line, the for loop is iterating over all the content in the HashMap.
final ObservableList<Map> data = FXCollections.observableArrayList();
data.addAll(HASHMAP);
TableColumn<Map.Entry, String> nCol = new TableColumn<Map.Entry, String>("Name");
nCol.setEditable(true);
nCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Entry, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Entry, String> p) {
Set <String> set=HASHMAP.keySet();
for (String key:HASHMAP.keySet())
{
String key1= key.toString();
return new SimpleObjectProperty<>(key.toString());
}
return null;
}
});
Table.setItems(data);
Table.getColumns().setAll(nCol,.........);

CellFactory.Callback.call() creates just one cell, not all cells in a loop
Using return from a loop breaks loop execution.
Take a look at next example, especially comments:
public class MapTableView extends Application {
#Override
public void start(Stage stage) {
// sample data
Map<String, String> map = new HashMap<>();
map.put("one", "One");
map.put("two", "Two");
map.put("three", "Three");
// use fully detailed type for Map.Entry<String, String>
TableColumn<Map.Entry<String, String>, String> column1 = new TableColumn<>("Key");
column1.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) {
// this callback returns property for just one cell, you can't use a loop here
// for first column we use key
return new SimpleStringProperty(p.getValue().getKey());
}
});
TableColumn<Map.Entry<String, String>, String> column2 = new TableColumn<>("Value");
column2.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<String, String>, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p) {
// for second column we use value
return new SimpleStringProperty(p.getValue().getValue());
}
});
ObservableList<Map.Entry<String, String>> items = FXCollections.observableArrayList(map.entrySet());
final TableView<Map.Entry<String,String>> table = new TableView<>(items);
table.getColumns().setAll(column1, column2);
Scene scene = new Scene(table, 400, 400);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

Sergey Grinev; I found a solution, a generic solution for this problem
public class TableCassaController<K,V> extends TableView<Map.Entry<K,V>> implements Initializable {
#FXML private TableColumn<K, V> column1;
#FXML private TableColumn<K, V> column2;
public TableCassaController(ObservableMap<K,V> map, String col1Name, String col2Name) {
System.out.println("Costruttore table");
TableColumn<Map.Entry<K, V>, K> column1 = new TableColumn<>(col1Name);
column1.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<K, V>, K>, ObservableValue<K>>() {
#Override
public ObservableValue<K> call(TableColumn.CellDataFeatures<Map.Entry<K, V>, K> p) {
// this callback returns property for just one cell, you can't use a loop here
// for first column we use key
return new SimpleObjectProperty<K>(p.getValue().getKey());
}
});
TableColumn<Map.Entry<K, V>, V> column2 = new TableColumn<>(col2Name);
column2.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map.Entry<K, V>, V>, ObservableValue<V>>() {
#Override
public ObservableValue<V> call(TableColumn.CellDataFeatures<Map.Entry<K, V>, V> p) {
// for second column we use value
return new SimpleObjectProperty<V>(p.getValue().getValue());
}
});
ObservableList<Map.Entry<K, V>> items = FXCollections.observableArrayList(map.entrySet());
this.setItems(items);
this.getColumns().setAll(column1, column2);
}
Very Thanks!!! :-)

Related

How to pass GetIntent data from Activity to ViewModel

I have LoaiSpActivity , DanhSachActivity and DanhSachSpViewModel for DanhSachActivity
So in LoaiSpActivity: i had putExtra a key & value called "idloaivatpham".
In DanhSachActivity: i have a function to get that key & value from key "idloaivatpham" into idkiem.
In DanhSachSpViewModel: i have a function to get data from Server but it will base on a Key & value (idkiem) to return data (in hash getParam()).
So now i want to pass the idkiem from DanhSachActivity to DanhSachSpViewModel but when i put idkiem = getIntent().getIntExtra("idloaivatpham", -1); in that ViewModel i got a error in getIntent(). So anyone have the solution please help me. Sorry for my english not very well.
Here is my code
LoaiSpActivity
private void CatchOnItemListView(List<LoaiVatPham> loaiVatPhams) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
switch (i){
case 0:
if(CheckConnection.haveNetworkConnection(getApplicationContext())){
Intent intent = new Intent(LoaiSpActivity.this, DanhSachSpActivity.class);
//truyen du lieu giua cac activity
intent.putExtra("idloaivatpham",loaiVatPhams.get(i).getId());
startActivity(intent);
}else{
CheckConnection.ShowToast(getApplicationContext(),"Check Internet Connection");
}
break;
case 1:
if(CheckConnection.haveNetworkConnection(getApplicationContext())){
Intent intent = new Intent(LoaiSpActivity.this, DanhSachSpActivity.class);
intent.putExtra("idloaivatpham",loaiVatPhams.get(i).getId());
startActivity(intent);
DanhSachActivity
private void GetIdLoaiSanPham() {
idkiem = getIntent().getIntExtra("idloaivatpham", -1);
Log.d("giatriloaisanpham", idkiem + "");
}
DanhSachSpViewModel
}) {
#Nullable
#Override
protected Map<String, String> getParams() throws AuthFailureError {
HashMap<String, String> param = new HashMap<String, String>();
param.put("idloaivatpham", String.valueOf(idkiem));
return param;
}
};
requestQueue.add(stringRequest);
}
My error when i put getIntent in ViewModel
idkiem = getIntent().getIntExtra("idloaivatpham", -1); // This have error at getIntent().

Hashmap Access from another class

I want to access Hashmap from another class for Rule writing how can i do that?
Class Nov{
public static main()
...
public static HashMap<Object, Object> parseJson(JSONObject jsonObject) throws ParseException {
Set<Object> set = jsonObject.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if (jsonObject.get(obj) instanceof JSONArray)
{
getArray(jsonObject.get(obj));
}
else {
}
}

HashMap is empty after deserialization with Jersey and Jackson

I have a REST web service using Jersey 1.17.1 and Jackson 1.9.2.
The API looks like this:
public class PlayerRequest {
private String language;
private String playerId;
private Map<String, String> params;
}
When this service is called by other component, the params map is received empty:
PlayerRequest [language=null, playerId=100036343, params={}]
Original request from other component:
PlayerRequest [language=null, playerId=100036343, params={context=mobile, countrycode=SE, partnerskin=8, locale=en_GB, ipaddress=62.209.186.13}]
Why is the HashMap empty after the deserialization?
In your PlayerRequest class, create a method annotated with #JsonAnySetter, as following:
#JsonAnySetter
public void set(String key, String value) {
params.put(key, value);
}
This method works as a fallback handler for all unrecognized properties found in the JSON content.
To use the above mentioned approach, ensure the params field is being initialized:
private Map<String, String> params = new HashMap<String, String>();
So, your PlayerRequest class would be like:
public class PlayerRequest {
private String language;
private String playerId;
private Map<String, String> params = new HashMap<String, String>();
public PlayerRequest() { }
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getPlayerId() {
return playerId;
}
public void setPlayerId(String playerId) {
this.playerId = playerId;
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
#JsonAnySetter
public void set(String key, String value) {
params.put(key, value);
}
}
Fixed by implementing and adaptor(javax.xml.bind.annotation.adapters.XmlAdapter) and annotated the map in api with #XmlJavaTypeAdapter

How to convert a tree structure to a Stream of nodes in java

I want to convert a tree in a Java8 stream of nodes.
Here is a tree of nodes storing data which can be selected:
public class SelectTree<D> {
private D data;
private boolean selected = false;
private SelectTree<D> parent;
private final List<SelectTree<D>> children = new ArrayList<>();
public SelectTree(D data, SelectTree<D> parent) {
this.data = data;
if (parent != null) {
this.parent = parent;
this.parent.getChildren().add(this);
}
}
public D getData() {
return data;
}
public void setData(D data) {
this.data = data;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public SelectTree<D> getParent() {
return parent;
}
public void setParent(SelectTree<D> parent) {
this.parent = parent;
}
public List<SelectTree<D>> getChildren() {
return children;
}
public boolean isRoot() {
return this.getParent() == null;
}
public boolean isLeaf() {
return this.getChildren() == null || this.getChildren().isEmpty();
}
}
I want to get a collection of the selected data
I want to do something like that:
public static void main(String[] args) {
SelectTree<Integer> root = generateTree();
List<Integer> selectedData = root.stream()
.peek(node -> System.out.println(node.getData()+": "+node.isSelected()))
.filter(node-> node.isSelected())
.map(node-> node.getData())
.collect(Collectors.toList()) ;
System.out.println("\nselectedData="+selectedData);
}
private static SelectTree<Integer> generateTree() {
SelectTree<Integer> n1 = new SelectTree(1, null);
SelectTree<Integer> n11 = new SelectTree(11, n1);
SelectTree<Integer> n12 = new SelectTree(12, n1);
n12.setSelected(true);
SelectTree<Integer> n111 = new SelectTree(111, n11);
n111.setSelected(true);
SelectTree<Integer> n112 = new SelectTree(112, n11);
SelectTree<Integer> n121 = new SelectTree(121, n12);
SelectTree<Integer> n122 = new SelectTree(122, n12);
return n1;
}
The problem was to find the implementation of stream() and I think I could help some people sharing my solution and I would be interested in knowing if there are some issues or better ways of doing this.
At first it was for primefaces TreeNode but I generalize the problem to all kinds of trees.
One small addition to kwisatz's answer.
This implementation:
this.getChildren().stream()
.map(SelectTree::stream)
.reduce(Stream.of(this), Stream::concat);
will be more eager, i. e. the whole hierarchy will be traversed during a stream creation. If your hirarchy is large and, let's say, you're looking for a single node matching some predicate, you may want a more lazy behaviour:
Stream.concat(Stream.of(this),
this.getChildren().stream().flatMap(SelectTree::stream));
In this case, only the children of the root node will be retrieved during a stream creation, and a search for a node won't necessarily result in the whole hierarchy being traversed.
Both approaches will exhibit the DFS iteration order.
I find this implementation of stream() which is a DFS tree traversal:
public class SelectTree<D> {
//...
public Stream<SelectTree<D>> stream() {
if (this.isLeaf()) {
return Stream.of(this);
} else {
return this.getChildren().stream()
.map(child -> child.stream())
.reduce(Stream.of(this), (s1, s2) -> Stream.concat(s1, s2));
}
}
}
If you can't change the tree implementation like for primefaces TreeNode (org.primefaces.model.TreeNode) you can define a method in an other class:
public Stream<TreeNode> stream(TreeNode parentNode) {
if(parentNode.isLeaf()) {
return Stream.of(parentNode);
} else {
return parentNode.getChildren().stream()
.map(childNode -> stream(childNode))
.reduce(Stream.of(parentNode), (s1, s2) -> Stream.concat(s1, s2)) ;
}
}
A more general approach using any node class is to add a parameter for the method, which returns the children:
public class TreeNodeStream {
public static <T> Stream<T> of(T node, Function<T, Collection<? extends T>> childrenFunction) {
return Stream.concat( //
Stream.of(node), //
childrenFunction.apply(node).stream().flatMap(n -> of(n, childrenFunction)));
}
}
An example using File:
TreeNodeStream.of(
new File("."), f -> f.isDirectory() ? Arrays.asList(f.listFiles()) :
Collections.emptySet())
.filter(f -> f.getName().endsWith(".java"))
.collect(Collectors::toList);

How to update TableView Row using javaFx

I'm trying to make some downloads and show the progress inside my table:
to do that I'm using the following classes:
public class DownloadDataTable {
private SimpleDoubleProperty progress;
private SimpleStringProperty type;
private SimpleStringProperty status;
public DownloadDataTable(double progress, String type, String status) {
this.progress = new SimpleDoubleProperty(progress);
this.type = new SimpleStringProperty(type);
this.status = new SimpleStringProperty(status);
}
public double getProgress() {
return progress.get();
}
public void setProgress(double progress) {
this.progress.set(progress);
}
public String getType() {
String retorno;
if (type==null){
retorno="";
}else{
retorno=type.get();
}
return retorno;
}
public void setType (String type) {
this.type.set(type);
}
public String getStatus(){
String retorno;
if (status==null){
retorno="";
} else{
retorno=status.get();
}
return retorno;
}
public void setStatus(String status){
this.status.set(status);
}
}
and to create the TitledPane, tableview and column tables I'm doing this:
public void addDownloadToTitledPane(DownloadContent downloadContent) {
MetaDados metaDado = downloadContent.getMetaDado();
String title = metaDado.getName();
if (title.length() > 60) {
title = title.substring(0, 57) + "...";
}
TableView downloadTable = new TableView();
TableColumn<DownloadDataTable, Double> progress = new TableColumn<>("progress");
progress.setCellFactory(new Callback<TableColumn<DownloadDataTable, Double>, TableCell<DownloadDataTable, Double>>() {
#Override
public TableCell<DownloadDataTable, Double> call(TableColumn<DownloadDataTable, Double> p) {
final ProgressBar progressBar = new ProgressBar(-1);
final TableCell cell = new TableCell<DownloadDataTable, Double>() {
#Override
protected void updateItem(Double t, boolean bln) {
super.updateItem(t, bln);
if (bln) {
setText(null);
setGraphic(null);
} else {
progressBar.setProgress(t);
progressBar.prefWidthProperty().bind(this.widthProperty());
setGraphic(progressBar);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
progress.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, Double>("progress"));
progress.setText("Progresso");
TableColumn<DownloadDataTable, String> type = new TableColumn<>("type");
type.setCellFactory(new Callback<TableColumn<DownloadDataTable, String>, TableCell<DownloadDataTable, String>>() {
#Override
public TableCell<DownloadDataTable, String> call(TableColumn<DownloadDataTable, String> p) {
TableCell cell = new TableCell<DownloadDataTable, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
type.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, String>("type"));
type.setText("Tipo");
TableColumn<DownloadDataTable, String> status = new TableColumn<>("status");
status.setCellFactory(new Callback<TableColumn<DownloadDataTable, String>, TableCell<DownloadDataTable, String>>() {
#Override
public TableCell<DownloadDataTable, String> call(TableColumn<DownloadDataTable, String> p) {
TableCell cell = new TableCell<DownloadDataTable, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
status.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, String>("status"));
status.setText("Status");
downloadTable.getColumns().addAll(progress, type, status);
List<PendingComponents> pendingComponents = downloadContent.getPendingComponents();
ObservableList<DownloadDataTable> data = FXCollections.observableArrayList();
for (PendingComponents pendingComponent : pendingComponents) {
String typeComponent;
if (pendingComponent.getType().equalsIgnoreCase(Constants.HTML)) {
typeComponent = "Conteúdo Principal";
} else {
typeComponent = "Pacote de Imagens";
}
data.add(new DownloadDataTable(-1, typeComponent, "Preparando download"));
}
downloadTable.setItems(data);
downloadTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TitledPane downloadPane = new TitledPane(title, downloadTable);
downloadPane.setId(metaDado.getOfflineUuid());
vBoxDownload.getChildren().add(downloadPane);
}
Until here everything seems to works fine, but when I try to recover my table and update the data, my table is not updated. I've debbuged and everything seems to work, even the data value is changed, but my table still without update. See my code:
private void publishProgress(final String msg) {
Platform.runLater(new Runnable() {
#Override
public void run() {
TitledPane titledPane = (TitledPane) controller.vBoxDownload.lookup("#"+metaDado.getOfflineUuid());
TableView table = (TableView) titledPane.getContent();
DownloadDataTable data = (DownloadDataTable) table.getItems().get(0);
data.setProgress(100);
data.setStatus(msg);
}
});
}
If I try to remove and add my row it doesn't work, but if I just add another row with the new values, I got a old row with the same value and a new row with new values. I can't figure out what am I doing wrong, someone can help me??
You shouldn't need to add/remove the row to get the table to update when the progress value changes.
The problem is that you're not making the progress property accessible to the TableView. This causes the progress.setCellValueFactory(...) call to wrap your getProgress() value in a new ObservableObjectWrapper. This allows the value to display in the TableView, but it won't notify the table when the value is changed.
Add the following to your DownloadDataTable class, and your table will update when the value changes:
public SimpleDoubleProperty progressProperty() {
return this.progress;
}
public SimpleStringProperty typeProperty() {
return this.type;
}
public SimpleStringProperty statusProperty() {
return this.status;
}

Resources