Items changed event for JavaFX ListView control - java

I am looking for something like an Item Changed Event or Item Count Changed Event for JavaFX ListView control or lets just say in general for any collection type control.
It is because, I have some Buttons, that I want to be enabled only when there is at least one item in ListView otherwise that Button should be in disabled state.
It is my guess that perhaps adding a ChangeListener to the ListView control. would that be a right approach.
Any suggestions how can we achieve this.

The JavaFX Listview provides a method with the signature
public final ObservableList<T> getItems()
You can add a listener to the observable list which will be called whenever items are added to or removed from the ListView.
aListView.getItems().addListener(new ListChangeListener() {
#Override
public void onChanged(ListChangeListener.Change change) {
System.out.println("Detected a change! ");
}
});
Similar functionality is also provided by the other 'collection' controls.

Add a binding based on the ListView items using Bindings.isEmpty
button.disableProperty().bind(Bindings.isEmpty(listView.getItems()));

This will also work when someone calls ListView#setItems and completely changes the observable list:
InvalidationListener updateScrollBarListener = ...
aListView.itemsProperty().addListener((obs, old, current) -> {
if(old != null) {
old.removeListener(updateScrollBarListener);
}
if(current != null) {
current.addListener(updateScrollBarListener);
}
});
I've been looking for a way to to make this a bit shorter (perhaps with Bindings) but haven't found it so far.

Related

Dynamically Create Menu Items based on an Observable List

I have an observable list of type Playlist.
I have a dropdown menu (connected to a MenuButton) with a MenuItem for each item in the list (plus a few default items I hard code that don't change).
For a TableView, I am able to link the data to an ObservableList, and the table's rows automatically update based on the contents of the List.
Is there a way to do the same thing for a MenuButton and its list of MenuItems?
I am not sure if there is built in support but either way, you can write a wrapper for any element you want based off an ObservableList by writing a listener. For example,
public class BoundMenuButton extends MenuButton {
ObservableList<MenuItem> items;
public BoundMenuButton(items) {
super(); // Not sure if needed
this.items = items;
// Listen for changes
items.addListener((ListChangeListener.Change<? extends MenuItem> change) -> {
updateItems();
});
}
public void updateItems() {
Platform.runLater( () -> {
// Do updates
});
}
}
This is just the very basics. You can extend this to have the same methods as a TableView fairly easily. (getItems(), setItems(), etc.).
P.S - This code is untested as I don't have access to a compiler currently.

ListView unable to add and edit cell when empty

I'm trying to use a ListView as an Editor for Strings, that come out of a custom data model. I use TextFieldListCells with an appropriate StringConverter for the cells.
There is an add button next to the ListView that calls this method on action:
#FXML
private void addElement() {
WordListItem newItem = new WordListItem(-1, "");
wordListItems.add(newItem);
wordListView.setEditable(true);
wordListView.getSelectionModel().select(wordListItems.indexOf(newItem));
wordListView.edit(wordListItems.indexOf(newItem));
wordListView.setEditable(false);
}
Where wordListView is the ListView and wordListItems is the ObservableList containing the data for the wordListView.
This does work, except for when the list is empty (not null), and I couldn't quite explain why, so I inspected the Java source code for help.
Here's what I found out so far: the edit(int) call on ListView changes the ListViews internal editIndex value, which is supposed to call the EDIT_START Event. The editIndex is an ReadOnlyIntegerWrapper in which I found some weird code that I can't quite understand and I'm not sure if thats actually producing a bug or I just can't see why they did it:
#Override
protected void fireValueChangedEvent() {
super.fireValueChangedEvent();
if (readOnlyProperty != null) {
readOnlyProperty.fireValueChangedEvent();
}
}
This method is called whenever the editIndex property of ListView is changed. The problem: readOnlyProperty is null, because it's not set anywhere. The only place I could find where it got set is in the getter:
public ReadOnlyIntegerProperty getReadOnlyProperty() {
if (readOnlyProperty == null) {
readOnlyProperty = new ReadOnlyPropertyImpl();
}
return readOnlyProperty;
}
(ReadOnlyIntegerImpl is an inner private class and readOnlyProperty is it's type)
Now to my actual question: Is this a bug or am I overseeing something? Is there a reason why I can't add and edit a newly created Element in my list like that when it's empty, or is it really just this getter not being called yet?
The source code you found just is code for lazy initializing the property.
Unless new value is assigned to the property or the property itself is requested, null can be used as the property to avoid unnecessary creation of property objects. This is not an issue here.
The issue seems to be the ListView cells not being updated before edit is called. This happens during layout, so "manually" calling layout before starting the edit should work:
private void addElement() {
WordListItem newItem = new WordListItem(-1, "");
wordListItems.add(newItem);
wordListView.setEditable(true);
wordListView.layout();
wordListView.edit(wordListItems.size()-1);
wordListView.setEditable(false);
}

JavaFx TableView itemProperty does not notifies ChangeListener

I have problem with my TableView element. I adding listener like that:
HardwareIdTableView.getItems().addListener(
(ListChangeListener.Change<? extends FirmwareData.HardwareIdWrapper> change) -> {
checker.hardwareIdCompleted.setValue(change.getList().size() > 0);
});
checker.hardwareIdCompleted is BooleanProperty.
I checked in debugger and new items was added to the TableView, but hardwareIdCompleted still resist 'false'.
P.S.
I add items to TableView like this:
public void addHardwareKey(HardwareIdKeyT key) {
ObservableList<FirmwareData.HardwareIdWrapper> idKeys = HardwareIdTableView.getItems();
if (idKeyEditSelected != null) {
fwData.removeHardwareIdKey(idKeyEditSelected.getIdPattern());
idKeys.remove(idKeyEditSelected);
}
if (!idKeys.contains(key)) {
HardwareIdTitledPane.pseudoClassStateChanged(PseudoClass.getPseudoClass("pane-error"), false);
idKeys.add(new FirmwareData.HardwareIdWrapper(key));
fwData.addHardwareIdKey(key);
}
}
The fault was straight - my reset function assigned new list to TableView. Since listener was assigned to old item list, it doesn't get notification, when I was expecting.

Updating a Wicket WebMarkupContainer

Inside my Wicket webpage, I have a WebMarkupContainer which contains a ListView:
notifications = new ArrayList<Notification>(...);
ListView listView = new ListView("notification", notifications) {
#Override
protected void populateItem(ListItem item) {
...
}
};
container = new WebMarkupContainer("container");
container.setOutputMarkupId(true);
container.add(listView);
this.add(container);
The WebMarkupContainer is in place in order to let me dynamically update the list of items shown to the user onscreen. This is possible when the user clicks on a link or by adding the container to incoming AjaxRequestTarget.
Now I'm required to update the list without having an Ajax request:
public void refresh() {
List<Notification> newNotifications = ...
notifications.addAll(0, newNotifications);
}
This method is called in a run-time environment and the list of notifications, which is a private field of my webpage (same one as last code), will contain new objects. I want these new items displayed to the user. Is it possible to update (or re-render) the container?
I'm new to Wicket so if you have a better way to achieve the same results, I would appreciate if you could share it with me.
You would have to do it on a timer. Use AjaxSelfUpdatingTimerBehavior to do so. Just set some sensible duration and add your container to target in 'onTimer()' method.
EDIT:
If your 'refresh()' function is only called when new notifications appear, you could set a flag on your page (define boolean variable on page and change it to true when new notification appears and to false once listView is refreshed). Then you can set short duration on the behavior and 'onTimer()' would look something like that:
onTimer(AjaxRequestTarget target) {
if(newNotifications) {
target.add(container);
newNotifications = false;
}
}
And refresh
public void refresh() {
List<Notification> newNotifications = ...
notifications.addAll(0, newNotifications);
newNotifiactions = true;
}
That way container won't be refreshed too often (which might cause strange effects) and will refresh every time new notification appears.

How to respond to click on a table row in vaadin

I've got the following code:
public Button getBtnSubmit(com.vaadin.ui.Button.ClickListener l) {
if (null != l) {
btnSubmit.addListener(l);
}
return btnSubmit;
}
public Table getTableCompany(HeaderClickListener hl) {
if (null != hl) {
tableCompany.addListener(hl);
}
return tableCompany;
}
I would like to add a listener that fires when I select a (different) row in the table.
This so that I can refresh some other controls with the table data, which listener should I use?
addListener is deprecated now. Use the following instead.
table.addItemClickListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent itemClickEvent) {
System.out.println(itemClickEvent.getItemId().toString());
}
});
I would go for ItemClickListener:
table.addListener(new ItemClickEvent.ItemClickListener() {
#Override
public void itemClick(ItemClickEvent event) {
//implement your logic here
}
});
edit: For Vaadin 7+, use addItemClickListener method instead of addListener.
You want to add a ValueChangeListener
If you use the ValueChangeListener don't forget to set
table.setImmediate(true);
This means that the browser will report a change on selection immediately. If you don't set this your listener is not called.
Read https://vaadin.com/book/-/page/components.table.html, section 5.15.1 "Selecting Items in a Table". You want to add a Property.ValueChangeListener.
Many of these answers are both correct, and incorrect.
If you need to get the selected items in response to the click, register a ValueChangeListener. Calling getValue() to retrieve the selection from the ItemClickListener might be 1 item behind in a MultiSelect list. For example, the set of items won't include/exclude the item triggering the callback. You will not have a reference to the clicked item however.
If you simply want to respond to a click on an item, and do not need to consider the current selection state, register an ItemClickListener instead. This way you will know what item was actually clicked.

Categories