JavaFX – ObservableList and list's item change - javafx

This answer provides a solution for an observable list that will send "list updated" notifications if properties of elements of the list change.
In my case, elements (a Element class) of such observable list are complex and I don't like to implement property for each member variable. Due to this, I added into the Element class a BooleanProperty that indicates change of the class.
Element Class
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class Element {
// ...
private ReadOnlyBooleanWrapper changeIndicatorWrapper;
public Element() {
//...
changeIndicatorWrapper = new ReadOnlyBooleanWrapper(false);
}
public ReadOnlyBooleanProperty changeIndicatorProperty() {
return changeIndicatorWrapper.getReadOnlyProperty();
}
public void someMethod() {
// Some update
changeIndicatorWrapper.set(!changeIndicatorWrapper.get());
}
}
Observable List
ObservableList<Element> elementsObservableList = FXCollections.observableList(
new ArrayList<>(),
(Element element) -> new Observable[] { element.changeIndicatorProperty() }
);
elementsObservableList.addListener(new ListChangeListener<Element>() {
#Override
public void onChanged(Change<? extends Element> c) {
System.out.println("CHANGE");
while(c.next()) {
if (c.wasUpdated()) {
for (int i = c.getFrom(); i < c.getTo(); ++i)
System.out.println(elementsObservableList.get(i));
}
}
}
});
My question is about this approach. Repeatedly set the changeIndicatorProperty to true not fire the change event. So, I need to reverse changeIndicatorProperty value changeIndicatorWrapper.set(!changeIndicatorWrapper.get()) each time. It is strange, isn't it?
Can I force programatically the update event?

It is strange, isn't it?
No this isn't surprising. For a change to be triggered a change needs to happen. If the BooleanProperty determines no change does happen and therefore the listeners are not notified of anything, this still satisfies the contract of Property.
Actually a Property isn't needed anyways. What is needed is a Observable that notifies it's observers. You could do this by using the following class and calling invalidate:
public class SimpleObservable implements Observable {
private final List<InvalidationListener> listeners = new LinkedList<>();
#Override
public void addListener(InvalidationListener listener) {
listeners.add(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
listeners.remove(listener);
}
public void invalidate() {
for (InvalidationListener listener : listeners) {
try {
listener.invalidated(this);
} catch (RuntimeException ex) {
}
}
}
}
Example:
public class Element {
protected final SimpleObservable observable = new SimpleObservable();
public Observable getObservable() {
return observable;
}
public static <T extends Element> ObservableList<T> observableArrayList() {
return FXCollections.observableArrayList(e -> new Observable[]{e.observable});
}
private void update() {
observable.invalidate();
}
public static void main(String[] args) {
ObservableList<Element> list = Element.observableArrayList();
list.addListener((ListChangeListener.Change<? extends Element> c) -> {
while (c.next()) {
if (c.wasUpdated()) {
System.out.println("update: [" + c.getFrom() + ", " + c.getTo() + ")");
}
}
});
list.addAll(new Element(), new Element(), new Element());
list.get(1).update();
}
}

Related

Javafx: Reduce events of checkable Listbox

I have a generic Javafx Listbox with checkable items (up to 20 items).
When I (de-)select an item, a property change is released to update other parts of the program.
It works all fine so far.
But I have additionally 2 buttons to (de-)select all items at once.
Also this working as expected, but I get an event for each item, which means that up to 20 property change events are fired, when one would be enough.
It is not a performance problem, not much is done on the events, but it is bad style.
Can anybody suggest a better solution?
public class FilterBox extends AnchorPane {
ListView<Item> listFilter;
Button buttonAll;
Button buttonNone;
public FilterBox() {
init();
place();
action();
getChildren().addAll(listFilter, buttonAll, buttonNone);
handleChange();
}
private void init() {
listFilter = new ListView<>();
buttonAll = new Button("All");
buttonNone = new Button("None");
}
private void place() {
setTopAnchor(listFilter, 0d);
setBottomAnchor(listFilter, 40d);
setLeftAnchor(listFilter, 0d);
setRightAnchor(listFilter, 0d);
setBottomAnchor(buttonAll, 5d);
setLeftAnchor(buttonAll, 0d);
buttonAll.setPrefWidth(75d);
setBottomAnchor(buttonNone, 5d);
setRightAnchor(buttonNone, 0d);
buttonNone.setPrefWidth(75d);
}
private void action() {
listFilter.setCellFactory(CheckBoxListCell.forListView((Item item) -> item.selectedProperty()));
buttonAll.setOnAction((t) -> {
changeAll(true);
});
buttonNone.setOnAction((t) -> {
changeAll(false);
});
}
private void changeAll(Boolean state) {
for (Item i : listFilter.getItems()) {
i.setSelected(state);
}
}
private void setListener(Item item) {
item.selectedProperty().addListener((o, ov, nv) -> {
Trigger.setNewFilterEvent();
});
}
private void handleChange() {
HashSet<String> list = InputData.getSportList();
for (String s : list) {
Item item = new Item(s, true);
listFilter.getItems().add(item);
setListener(item);
}
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final BooleanProperty selected = new SimpleBooleanProperty();
public Item(String name, boolean on) {
setName(name);
setSelected(on);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final BooleanProperty selectedProperty() {
return this.selected;
}
public final boolean isSelected() {
return this.selectedProperty().get();
}
public final void setSelected(final boolean sel) {
this.selectedProperty().set(sel);
}
#Override
public String toString() {
return getName();
}
}
}
Assuming Trigger.setNewFilterEvent() is what you want called only once when the "select all" or the "deselect all" actions are fired, then you can use a boolean flag for this.
public class FilterBox {
private boolean ignoreIndividualChanges;
// other fields omitted for brevity
private void changeAll(boolean state) {
ignoreIndividualChanges = true;
try {
for (Item i : listFilter.getItems()) {
i.setSelected(state);
}
Trigger.setNewFilterEvent(); // fire one event
} finally {
ignoreIndividualChanges = false;
}
}
private void setListener(Item item) {
item.selectedProperty().addListener((obs, ov, nv) -> {
if (!ignoreIndividualChanges) {
Trigger.fireNewFilterEvent();
}
});
}
// other methods omitted for brevity
}
Also, note I changed the parameter type for changeAll from Boolean to boolean. There's no reason to use the reference type here, so you should stick with the primitive type.

UI updates are getting blocked by future.get() in javafx

I have a function which is supposed to return a list from the result of a Task API.
#Override
public List performQuery(boolean isPaginationQuery, boolean isSortingQuery {
try {
TaskImpl taskImpl = new TaskImpl(isPaginationQuery,
isSortingQuery);
queryExecutor.submit(taskImpl).get();
return taskImpl.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Inner class which performs the updates
private class TaskImpl extends Task<List> {
private boolean isPaginationQuery, isSortingQuery;
public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery) {
this.isPaginationQuery = isPaginationQuery;
this.isSortingQuery = isSortingQuery;
}
#Override
protected List call() throws Exception {
Platform.runLater(() -> {
loaderContainer.setVisible(true);
loaderContainer.toFront();
});
HSession hSession = new HSession();
TaskInfoDao taskInfoDao = new TaskInfoDaoImpl(hSession.getSession(), currentConnection.getConnectionId());
if (!isPaginationQuery && !isSortingQuery) {
paginator.setTotal(taskInfoDao.getTaskInfoWithFiltersCount(paginator.getFilterMap(), false));
}
Stream<TaskInfo> resultStream = taskInfoDao.getTaskInfoWithFilters(paginator.getFilterMap(), false,
paginator.getStartIndex() * paginator.getPageSize(),
paginator.getPageSize() * paginator.getPageGap());
List<TaskInfoTableView> data = createData(resultStream);
hSession.close();
return data;
}
#Override
protected void succeeded() {
super.succeeded();
try {
//set the pagination if the task is complete
//and it is not a pagination query
if (!isPaginationQuery) {
((TaskInfoViewController) uiController).setPagination(
FXCollections.observableArrayList(get()));
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
#Override
protected void cancelled() {
super.cancelled();
updateMessage("Cancelled!");
}
#Override
protected void failed() {
super.failed();
updateMessage("Failed!");
}
}
performQuery function calls the thread and waits for its result.
The loader is being displayed from inside the TaskImpl class using Platform.runLater.
But the loader does not appear until the task has finished i.e. loader appears after the completion of call() function's execution.
When i remove the taskImpl.get() the loader works fine.
Any help is appreciated.
P.S. : Under any case, I need the result of the Task API outside the Inner class( outside TaskImpl )
First of all, it seems like you are not very familiar with asynchronous programming. Having performQuery() to return a List shows that you are expecting to run this synchronously - there is no way for you to return results before you get the results. This is exactly why you are freezing your UI.
The important thing to understand about asynchronous programming is, you would start doing something (i.e. a task) in another thread, and return immediately. When there is result returned from the task, you switch back to the UI (JavaFX Application) thread to update it. You can see this as event-driven approach.
Therefore, for your case, you should directly update the list (the list which you are returning in performQuery()) in the succeeded() method that you have overridden in TaskImpl class.
If the list that you should be updating is not in the scope of TaskImpl, then you can the functional interfaces in java.util.function package to do it for you. This means that you would create that functional interface object at the right scope, and pass in into TaskImpl during object construction, and call that interface in succeeded().
Update
If I assume this is what calls performQuery():
public class MyController {
#FXML
TableView<Foo> tableView;
public void initialize() {
List result = queryController.performQuery(true, true);
tableView.getItems().addAll(result);
}
}
Then, I would probably do something like this:
public class MyController {
#FXML
TableView<Foo> tableView;
public void initialize() {
List result = queryController.performQuery(true, true, list -> tableView.getItems.addAll(list));
}
}
public class QueryController {
#Override
public void performQuery(boolean isPaginationQuery, boolean isSortingQuery, java.util.function.Consumer<List> onQuerySucceeded) {
try {
TaskImpl taskImpl = new TaskImpl(isPaginationQuery,
isSortingQuery, onQuerySucceeded);
queryExecutor.submit(taskImpl);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
private class TaskImpl extends Task<List> {
private final java.util.function.Consumer<List> onQuerySucceeded;
public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery, java.util.function.Consumer<List> onQuerySucceeded) {
this.isPaginationQuery = isPaginationQuery;
this.isSortingQuery = isSortingQuery;
this.onQuerySucceeded = onQuerySucceeded;
}
#Override
protected void succeeded() {
super.succeeded();
// Not sure what the original codes are doing.
try {
//set the pagination if the task is complete
//and it is not a pagination query
if (!isPaginationQuery) {
((TaskInfoViewController) uiController).setPagination(
FXCollections.observableArrayList(get()));
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// This is what is being added in
onQuerySucceeded.accept(this.getValue());
}
}

JavaFX/TableView: 3 different tables use the same TableCellDouble, but 3x TableCellDouble classes necessary ... how to reduce to 1x?

I have in my application 3 different tables that contain double-value columns with their own TableCell.
The input and display of the double values is the same in all three tables.
Unfortunately, I had to create the identical TableCellDouble class 3x, only because of the different 1st code line.
//3x different data class with the getters and setters
DataLineExpectedValue.java
DataLineInputMoney.java
DataLinePayPosition.java
//3x TableCellDouble, although these are the same except for the first line
TableCellDouble_expectedValue.java:
public class TableCellDouble_expectedValue extends TableCell<DataLineExpectedValue, String> { //for DataLineExpectedValue
private MyTextFieldOnlyDoubleWithComma textFieldOnlyDouble = new MyTextFieldOnlyDoubleWithComma();
public TableCellDouble_expectedValue() { ... }
#Override
protected void updateItem(String item, boolean empty) { ... }
#Override
public void startEdit() { ... }
#Override
public void commitEdit(String newValue) { ... }
#Override
public void cancelEdit() { ...}
}
TableCellDouble_inputMoney.java:
public class TableCellDouble_inputMoney extends TableCell<DataLineInputMoney, String> { //for DataLineInputMoney
The rest is the same code as above.
...
}
TableCellDouble_payPosition.java:
public class TableCellDouble_payPosition extends TableCell<DataLinePayPosition, String> { //for DataLinePayPosition
The rest is the same code as above.
...
}
//Question:
//How to get the 3 almost same classes:
//TableCellDouble_expectedValue.java,
//TableCellDouble_inputMoney.java and
//TableCellDouble_payPosition.java
//=> in a class called TableCellDouble.java
//And then use it uniformly in all tables in the application.
//E.g. Instead of:
table01Column01.setCellFactory( (param) -> { return new TableCellDouble_inputMoney(); });
table02Column04.setCellFactory( (param) -> { return new TableCellDouble_expectedValue(); });
table03Column11.setCellFactory( (param) -> { return new TableCellDouble_payPosition(); });
//Then uniformly so:
table01Column01.setCellFactory( (param) -> { return new TableCellDouble(); });
table02Column04.setCellFactory( (param) -> { return new TableCellDouble(); });
table03Column11.setCellFactory( (param) -> { return new TableCellDouble(); });
Use generic definition
public class TableCellDouble<T> extends TableCell<T, String> {
... your code
}

JSF custom panel with button - action not invoked

I have built a custom component button, but somehow the action is not invoked. When debugging the getAction-Method within the component and invoking the supplied MethodeExpression the Bean-Method is called as expected. But due to some reason, the Expression is not invoked when pressing the button in the browser.
Is there some kind of additional Interface necessary to pass the action to the embedded button-component?
Any help is very appreciated since I am stuck at this issue for some days now
MyClass:
public class MyClass extends UIPanel implements SystemEventListener
{
private UIForm form;
private HtmlCommandButton buttonOk;
public MyClass()
{
FacesContext context = getFacesContext();
UIViewRoot root = context.getViewRoot();
root.subscribeToViewEvent(PostAddToViewEvent.class, this);
}
#Override
public void processEvent(SystemEvent event)
{
this.form = new UIForm();
this.buttonOk = new HtmlCommandButton();
this.buttonOk.setId("okButtonId");
this.buttonOk.setActionExpression(getAction());
this.buttonOk.setValue("OK");
this.form.getChildren().add(this.buttonOk);
getChildren().add(this.form);
}
private enum PropertyKeys
{
action, text, titel
}
public MethodExpression getAction()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.action);
}
public void setAction(MethodExpression actionExpression)
{
getStateHelper().put(PropertyKeys.action, actionExpression);
}
public String getText()
{
return (String) getStateHelper().eval(PropertyKeys.text);
}
public void setText(String text)
{
getStateHelper().put(PropertyKeys.text, text);
}
public String getTitel()
{
return (String) getStateHelper().eval(PropertyKeys.titel);
}
public void setTitel(String titel)
{
getStateHelper().put(PropertyKeys.titel, titel);
}
#Override
public void encodeAll(FacesContext context) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement(HTML.DIV_ELEM, this);
writer.writeText(getText(), null);
this.form.encodeAll(context);
writer.endElement(HTML.DIV_ELEM);
}
#Override
public void encodeChildren(FacesContext context) throws IOException
{
}
#Override
public boolean isListenerForSource(Object source)
{
return (source instanceof MyClass);
}
}
MyClassHandler:
public class MyClassHandler extends ComponentHandler
{
public MyClassHandler(ComponentConfig config)
{
super(config);
}
#SuppressWarnings("rawtypes")
#Override
protected MetaRuleset createMetaRuleset(Class type)
{
return super.createMetaRuleset(type).addRule(new MethodRule("action", String.class, new Class[] { ActionEvent.class }));
}
}
myView Method:
...
public String myMethod()
{
System.err.println("myMethod");
return "/some/path/yadayada.xhtml";
}
...
MyView.xhtml
<myTag action="#{myView.myMethod}" id="id1" titel="bla" text="bleh" />
Exdending UICommand is enough, since you only want one action to be executed.
You have to provide two additional MethodExpressions via the tag-attributes and within the decode-method you can check which button has been pressed and redirect the particular MethodExpression to the standard-action provided by UICommand. This way, you dont have to worry about the legacy-interface ActionSource, or how Events are broadcasted.
public void decode(FacesContext contex)
{
Map<String,String> map = context.getExternalContext.getRequestParameterMap();
// your rendered buttons need a name you check for
final boolean okPressed = map.containsKey( getClientId + ":ok" );
final boolean cancelPressed = map.containsKey( getClientId + ":cancel" );
if(okPressed || cancelPressed)
{
MethodExpression exp = null;
if(okPressed)
{
exp = getActionOk();
}
else
{
exp = getActionCancel();
}
// redirect to standard action
setActionExpression(exp);
queueEvent(new ActionEvent(this));
}
}
In order to make use of of this you need two attributes (actionOk and actionCancel) which use Method Expressions (setter and getter). Those have to be configured by a ComponentHandler as you did for the action-attribute.

multiple IStateManager in StateManagedCollection

I was reading a post at VS 2008, ASP.NET: Generate Local Resources.
Mehdi Golchin showed us a beautiful job of StateManagedCollection.
However I was wondered about using multiple classes of IStateManager in one StateManagedCollection.
As you can see below:
public class MenuItemCollection : StateManagedCollection
{
public MenuItem this[int index]
{
get { return (MenuItem)((IList)this)[index]; }
}
public int Add(MenuItem item)
{
return ((IList)this).Add(item);
}
public void Remove(MenuItem item)
{
((IList)this).Remove(item);
}
// Write Insert and RemoveAt methods
protected override void SetDirtyObject(object o)
{
((MenuItem)o).SetDirty();
}
}
This MenuItemCollection class can have only one child class("MenuItem").
If I want to use another class as well as MenuItem class, for example MenuItem2 class, how do I have to write the codes?
Anyone can help me?
Thanks in advance.
Write a generic version - for example,
public class GenericStateManagedCollection<T> : StateManagedCollection
where T: IStateManager, new()
{
public T this[int index]
{
get { return (T)((IList)this)[index]; }
}
public int Add(T item)
{
return ((IList)this).Add(item);
}
public void Remove(T item)
{
((IList)this).Remove(item);
}
// Write Insert and RemoveAt methods
protected override void SetDirtyObject(object o)
{
((T)o).SetDirty();
}
protected override object CreateKnownType(int index)
{
return Activator.CreateInstance<T>();
}
protected override Type[] GetKnownTypes()
{
return new Type[] { typeof(T) };
}
}
And use it as
public class MenuItemCollection : GenericStateManagedCollection<MenuItem> { }
public class XyzItemCollection : GenericStateManagedCollection<XyzItem> { }
EDIT:
I have most probably mis-understood your question! Assuming now that you want to put two different type of objects into the StateManagedCollection. From usage perspective, it doesn't make sense to have objects of completely unrelated types into the collection - you need to have some base class. For example, consider DataControlFieldCollection which holds instances of (abstract) type 'DataControField. BoundField, ButtonField etc inherits fromDataControField`.
So you need to go via similar route - for example,
public class MenuItemBase : IStateManager
{
// Use implementation from link you quoted (Mehdi Golchin's answer)
...
}
public class MenuItem : MenuItemBase
{
...
}
public class MenuItem2 : MenuItemBase
{
...
}
public class MenuItemCollection : StateManagedCollection
{
public MenuItemBase this[int index]
{
get { return (MenuItemBase)((IList)this)[index]; }
}
public int Add(MenuItemBaseitem)
{
return ((IList)this).Add(item);
}
public void Remove(MenuItemBaseitem)
{
((IList)this).Remove(item);
}
// Write Insert and RemoveAt methods
protected override void SetDirtyObject(object o)
{
((MenuItemBase)o).SetDirty();
}
// important to override CreateKnownType and GetKnownTypes
private static readonly Type[] _knownTypes = new Type[] {typeof(MenuItem), typeof(MenuItem2) }
protected override Type[] GetKnownTypes()
{
return _knownTypes;
}
protected override object CreateKnownType(int index)
{
switch (index)
{
case 0:
return new MenuItem();
case 1:
return new MenuItem2();
default:
throw new Exception("Invalid Index");
}
}
}
Note: Untested code

Resources