I am trying to set items to a tableview but the setitems method expects an observablelist while I have an observableset in my model.The FXCollections utility class does not have a method for creating an observable list given an observable set.I tried casting but that caused a class cast exception (as expected).
Currently I am using this kind of code
new ObservableListWrapper<E>(new ArrayList<E>(pojo.getObservableSet()));
And I have some problems with it:
So in short I need a style guide or best practice for converting between observable set and observable list because I expect to be doing this a lot when building a java fx GUI
public class FXCollections extends Object. Utility class that consists of static methods that are 1:1 copies of java. util. Collections methods.
ArrayList: Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. ObservableList: A list that allows listeners to track changes when they occur.
Will editing this in the table update the underlying set as expected ?
No because, you are doing a copy of the set:
new ArrayList<E>(pojo.getObservableSet())
Is it the 'right' way of doing this ?
I think the right way is not doing that. Set
are not List
and vice versa. Both have specific contraints. For example, the lists are ordered and sets contains no duplicate elements.
Moreover, nor FXCollections
neither Bindings
provides this kind of stuff.
I would like the collection to remain as a set to enforce uniqueness
I guess you could write a custom ObservableList
, for example the Parent::children
have a similar behavior. It throws an IllegalArgumentException
if a duplicate children is added. If you look at the source code, you will see that it is a VetoableListDecorator
extension. You could write your own:
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.sun.javafx.collections.VetoableListDecorator;
public class CustomObservableList<E> extends VetoableListDecorator<E> {
public CustomObservableList(ObservableList<E> decorated) {
super(decorated);
}
@Override
protected void onProposedChange(List<E> toBeAdded, int... indexes) {
for (E e : toBeAdded) {
if (contains(e)) {
throw new IllegalArgumentException("Duplicament element added");
}
}
}
}
class Test {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Set<Object> set = new HashSet<Object>();
set.add(o1);
CustomObservableList<Object> list = new CustomObservableList<Object>(FXCollections.observableArrayList(set));
list.add(o2);
list.add(o1); // throw Exception
}
}
Just in Case someone stumbles over this question looking for a one-way to convert an ObservableSet into an ObservableList... I post my solution. It doesn't support feeding back data to the set (which in my opinion wouldn't be nice since TableView doesn't have a concept of not being able to change a value) but supports updates of the set and preserves the (in this case) sorted order.
package de.fluxparticle.lab;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Collections;
import java.util.Random;
import java.util.TreeSet;
import static javafx.collections.FXCollections.observableSet;
/**
* Created by sreinck on 23.01.17.
*/
public class Set2List extends Application {
private final ObservableSet<Integer> setModel = observableSet(new TreeSet<Integer>());
@Override
public void start(Stage primaryStage) throws Exception {
TableView<Integer> tableView = new TableView<>();
addColumn(tableView, "Number");
ObservableList<Integer> list = convertSetToList(setModel);
tableView.setItems(list);
Random rnd = new Random();
scheduleTask(Duration.millis(1000), () -> setModel.add(rnd.nextInt(10)));
primaryStage.setScene(new Scene(tableView, 800, 600));
primaryStage.setTitle("Set2List");
primaryStage.show();
}
private static void scheduleTask(Duration interval, Runnable task) {
Timeline timeline = new Timeline(new KeyFrame(interval, event -> task.run()));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
private static ObservableList<Integer> convertSetToList(ObservableSet<Integer> set) {
ObservableList<Integer> list = FXCollections.observableArrayList(set);
set.addListener((SetChangeListener<Integer>) change -> {
if (change.wasAdded()) {
Integer added = change.getElementAdded();
int idx = -Collections.binarySearch(list, added)-1;
list.add(idx, added);
} else {
Integer removed = change.getElementRemoved();
int idx = Collections.binarySearch(list, removed);
list.remove(idx);
}
});
return list;
}
private static void addColumn(TableView<Integer> tableView, String text) {
TableColumn<Integer, String> column = new TableColumn<>(text);
column.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().toString()));
tableView.getColumns().add(column);
}
public static void main(String[] args) {
launch(args);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With