Implementing an ObservableValue - javafx

I have this object:
public class Oggetto{
private int value;
private boolean valid;
public Oggetto(int value, boolean valid) {
this.value = value;
this.valid = valid;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
and I would like implement an Observable object that fires events when something inside changes
Here the observable object:
public class OggettoOsservabile implements ObservableValue<Oggetto>{
private Oggetto value;
OggettoOsservabile(int i, boolean b) {
this.value=new Oggetto(i, b);
}
#Override
public void addListener(ChangeListener<? super Oggetto> listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeListener(ChangeListener<? super Oggetto> listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public Oggetto getValue() {
return value;
}
#Override
public void addListener(InvalidationListener listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeListener(InvalidationListener listener) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
i dont know how to proceed in order to detect a change in the class "Oggetto" and send a notification to the registeres listener.
OggettoOsservabile oggetto = new OggettoOsservabile(1, false);
oggetto.addListener(new ChangeListener<Oggetto>() {
public void changed(ObservableValue<? extends Oggetto> observable, Oggetto oldValue, Oggetto newValue) {
System.out.println("changed " + oldValue + "->" + newValue);
}
});

Implement your Oggetto class using standard JavaFX Properties:
import javafx.beans.property.BooleanProperty ;
import javafx.beans.property.IntegerProperty ;
import javafx.beans.property.SimpleBooleanProperty ;
import javafx.beans.property.SimpleIntegerProperty ;
public class Oggetto {
private final IntegerProperty value = new SimpleIntegerProperty() ;
public final IntegerProperty valueProperty() {
return value ;
}
public final int getValue() {
return value.get();
}
public final void setValue(int value) {
this.value.set(value);
}
private final BooleanProperty valid = new SimpleBooleanProperty();
public final BooleanProperty validProperty() {
return valid ;
}
public final boolean isValid() {
return valid.get();
}
public final void setValid(boolean valid) {
this.valid.set(valid);
}
public Oggetto(int value, boolean valid) {
setValue(value);
setValid(valid);
}
}
This may be all you need, as you can just observe the individual properties. But if you want a class that notifies invalidation listeners if either property changes, you can extend ObjectBinding:
import javafx.beans.binding.ObjectBinding ;
public class OggettoObservable extends ObjectBinding {
private final Oggetto value ;
public OggettoObservable(int value, boolean valid) {
this.value = new Oggetto(value, valid);
bind(this.value.valueProperty(), this.value.validProperty());
}
#Override
public Oggetto computeValue() {
return value ;
}
}

import javafx.beans.InvalidationListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class VerySimply implements ObservableValue<Integer> {
private int newValue;
public ChangeListener<Integer> listener = new ChangeListener<Integer>() {
#Override
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
System.out.println(" :) "+ newValue.intValue());
}
};
#Override
public void addListener(ChangeListener<? super Integer> listener) {
}
#Override
public void removeListener(ChangeListener<? super Integer> listener) {
}
#Override
public Integer getValue() {
return newValue;
}
#Override
public void addListener(InvalidationListener listener) {
}
#Override
public void removeListener(InvalidationListener listener) {
}
public void setNewValue(int newValue) {
int oldValue = this.newValue;
this.newValue = newValue;
listener.changed(this,oldValue,this.newValue);
}
}

Related

ViewPager returning empty array

When I start my app I want my customSwipeAdapter.java to wait until my savedImages ArrayList has received and been populated with the data from firebase. But instead my class is being ran and my whole page is empty because getCount() method is returning savedImages.size() as 0 because my arraylist hasn't been populated in time. Any help on maybe running my class when my array list is populated. Not sure what to do here :)
customSwipeAdapter.java
public class customSwipeAdapter extends PagerAdapter {
private Firebase mRef;
private Context ctx;
private LayoutInflater layoutInflator;
public customSwipeAdapter(Context ctx) {
this.ctx = ctx;
}
private int[] frontImages = {R.drawable.amen_parham, R.drawable.janel_parham, R.drawable.kevin_parham};
// Populate ArrayList with firebase data
List<String> savedImages = new ArrayList<String>();
Boolean goingToCallOnce = false;
Boolean finishedLoadingData = false;
#Override
public int getCount() {
return savedImages.size();
}
#Override
public boolean isViewFromObject(View view, Object o) {
return (view == o);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
getSavedImages_FromDB();
layoutInflator = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View item_view = layoutInflator.inflate(R.layout.swipe_layout, container, false);
final EasyFlipView mYourFlipView = (EasyFlipView) item_view.findViewById(R.id.flipView);
ImageView imageView_Front = (ImageView) item_view.findViewById(R.id.imageView_Front);
imageView_Front.setImageResource(frontImages[position]);
container.addView(item_view);
System.out.println(savedImages);
return item_view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((RelativeLayout)object);
}
public void getSavedImages_FromDB() {
mRef = new Firebase("");
if (goingToCallOnce == false) {
goingToCallOnce = true;
mRef.child("Q6i3fI6lNdYYS0z5Jty4WUYE9g13").child("SavedImages").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String savedImage = (String) dataSnapshot.child("Image").getValue();
savedImages.add(0, savedImage);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
finishedLoadingData = true;
System.out.println("finishedLoadingData");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
}
savedCardsViewController.java
public class savedCardsViewController extends AppCompatActivity {
private Swipe swipe;
ViewPager viewPager;
customSwipeAdapter adapter;
private Firebase mRef;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_saved_cards_view_controller);
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new customSwipeAdapter(this);
viewPager.setAdapter(adapter);
viewPager.setPageTransformer(false, new DefaultTransformer());
}
}
'Context' to 'ValueEventListener'
2'nd 'Context' to 'ValueEventListener'
I suggest you load the data from Firebase on your Activity first and then pass it as a parameter to the adapter's constructor. This way your CustomSwipeAdapter would look similar to this:
public class customSwipeAdapter extends PagerAdapter {
private Firebase mRef;
private Context ctx;
private LayoutInflater layoutInflator;
List<String> savedImages = new ArrayList<String>();
public customSwipeAdapter(Context ctx, List<String> savedImages){
this.ctx = ctx;
this.savedImages = savedImages
}
...
}
Another note on Loading data from firebase on the Activity: use A SingleValueListener with an iterator instead of onChildAdded:
mRef.child("Q6i3fI6lNdYYS0z5Jty4WUYE9g13").child("SavedImages").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterator<DataSnapshot> data = dataSnapshot.getChildren().iterator();
while(data.hasNext())
{
String savedImage = (String) data.next().child("Image").getValue();
savedImages.add(0, savedImage);
}
//Data has finished loading. Load your adapter
adapter = new customSwipeAdapter(this, savedImages);
viewPager.setAdapter(adapter);
viewPager.setPageTransformer(false, new DefaultTransformer());
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});

mirror one observableList to another

In JavaFX, I have an ObservableList of objects, and want another ObservableList that will mirror the first list but contain a String representation of each object. Is there anything simpler than writing a custom ListChangeListener to do the conversion ? I have a StringConverter which can provide the mirrored value.
Similarly, given an ObservableList<String>, how do I create a second ObservableList<String> that has a constant entry at index 0, and mirrors the first list beginning at index 1?
For the first question, the easiest way to do this is to use the EasyBind framework. Then it is as simple as
ObservableList<String> stringList = EasyBind.map(myBaseList, myConverter::toString);
Here is an SSCCE using EasyBind:
import org.fxmisc.easybind.EasyBind;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.util.StringConverter;
public class MappedAndTransformedListExample {
public static void main(String[] ags) {
ObservableList<Person> baseList = FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com")
);
StringConverter<Person> converter = new StringConverter<Person>() {
#Override
public String toString(Person person) {
return person.getFirstName() + " " + person.getLastName();
}
#Override
public Person fromString(String string) {
int indexOfDelimiter = string.indexOf(' ');
return new Person(string.substring(0, indexOfDelimiter),
string.substring(indexOfDelimiter+1),
"");
}
};
ObservableList<String> namesList = EasyBind.map(baseList, converter::toString);
namesList.forEach(System.out::println);
namesList.addListener((Change<? extends String> c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println("Added "+c.getAddedSubList());
}
}
});
System.out.println("\nAdding Michael to base list...\n");
baseList.add(new Person("Michael", "Brown", "michael.brown#example.com"));
namesList.forEach(System.out::println);
}
public static class Person {
private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
private final StringProperty email = new SimpleStringProperty(this, "email");
public Person(String firstName, String lastName, String email) {
this.firstName.set(firstName);
this.lastName.set(lastName);
this.email.set(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final java.lang.String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final java.lang.String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final java.lang.String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final java.lang.String email) {
this.emailProperty().set(email);
}
}
}
If you prefer not to use a third-party framework for some reason, you can use a TransformationList (which is what EasyBind does under the hood: I copied the code below from the source code there and modified it).
In the above, you would replace
ObservableList<String> namesList = EasyBind.map(baseList, converter::toString);
with
ObservableList<String> namesList = new TransformationList<String, Person>(baseList) {
#Override
public int getSourceIndex(int index) {
return index ;
}
#Override
public String get(int index) {
return converter.toString(getSource().get(index));
}
#Override
public int size() {
return getSource().size();
}
#Override
protected void sourceChanged(Change<? extends Person> c) {
fireChange(new Change<String>(this) {
#Override
public boolean wasAdded() {
return c.wasAdded();
}
#Override
public boolean wasRemoved() {
return c.wasRemoved();
}
#Override
public boolean wasReplaced() {
return c.wasReplaced();
}
#Override
public boolean wasUpdated() {
return c.wasUpdated();
}
#Override
public boolean wasPermutated() {
return c.wasPermutated();
}
#Override
public int getPermutation(int i) {
return c.getPermutation(i);
}
#Override
protected int[] getPermutation() {
// This method is only called by the superclass methods
// wasPermutated() and getPermutation(int), which are
// both overriden by this class. There is no other way
// this method can be called.
throw new AssertionError("Unreachable code");
}
#Override
public List<String> getRemoved() {
ArrayList<String> res = new ArrayList<>(c.getRemovedSize());
for(Person removedPerson: c.getRemoved()) {
res.add(converter.toString(removedPerson));
}
return res;
}
#Override
public int getFrom() {
return c.getFrom();
}
#Override
public int getTo() {
return c.getTo();
}
#Override
public boolean next() {
return c.next();
}
#Override
public void reset() {
c.reset();
}
});
}
};
For the second question, you must use a transformation list. Here's an updated main(...) method that shows how to do this. (It works just as well with the second version of part 1.)
public static void main(String[] ags) {
ObservableList<Person> baseList = FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com")
);
StringConverter<Person> converter = new StringConverter<Person>() {
#Override
public String toString(Person person) {
return person.getFirstName() + " " + person.getLastName();
}
#Override
public Person fromString(String string) {
int indexOfDelimiter = string.indexOf(' ');
return new Person(string.substring(0, indexOfDelimiter),
string.substring(indexOfDelimiter+1),
"");
}
};
ObservableList<String> namesList = EasyBind.map(baseList, converter::toString);
ObservableList<String> namesListWithHeader = new TransformationList<String, String>(namesList) {
#Override
public int getSourceIndex(int index) {
return index - 1 ;
}
#Override
public String get(int index) {
if (index == 0) {
return "Contacts";
} else {
return getSource().get(index - 1);
}
}
#Override
public int size() {
return getSource().size() + 1 ;
}
#Override
protected void sourceChanged(Change<? extends String> c) {
fireChange(new Change<String>(this) {
#Override
public boolean wasAdded() {
return c.wasAdded();
}
#Override
public boolean wasRemoved() {
return c.wasRemoved();
}
#Override
public boolean wasReplaced() {
return c.wasReplaced();
}
#Override
public boolean wasUpdated() {
return c.wasUpdated();
}
#Override
public boolean wasPermutated() {
return c.wasPermutated();
}
#Override
public int getPermutation(int i) {
return c.getPermutation(i - 1) + 1;
}
#Override
protected int[] getPermutation() {
// This method is only called by the superclass methods
// wasPermutated() and getPermutation(int), which are
// both overriden by this class. There is no other way
// this method can be called.
throw new AssertionError("Unreachable code");
}
#Override
public List<String> getRemoved() {
ArrayList<String> res = new ArrayList<>(c.getRemovedSize());
for(String removed: c.getRemoved()) {
res.add(removed);
}
return res;
}
#Override
public int getFrom() {
return c.getFrom() + 1;
}
#Override
public int getTo() {
return c.getTo() + 1;
}
#Override
public boolean next() {
return c.next();
}
#Override
public void reset() {
c.reset();
}
});
}
};
namesListWithHeader.forEach(System.out::println);
namesListWithHeader.addListener((Change<? extends String> c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println("Added "+c.getAddedSubList());
System.out.println("From: "+c.getFrom()+", To: "+c.getTo());
}
}
});
System.out.println("\nAdding Michael to base list...\n");
baseList.add(new Person("Michael", "Brown", "michael.brown#example.com"));
namesListWithHeader.forEach(System.out::println);
}
Here's a slightly shorter version of James_D's answer using Lombok's #Delegate to avoid having to write all the delegating methods
/**
* A List that mirrors a base List by applying a converter on all items.
* #param <E> item type of the target List
* #param <F> item type of the base list
*/
public class MirroringList<E, F> extends TransformationList<E, F> implements ObservableList<E> {
/** mapping function from base list item type to target list item type */
private final Function<F, E> converter;
public MirroringList(ObservableList<? extends F> list, Function<F, E> converter) {
super(list);
this.converter = converter;
}
#Override
public int getSourceIndex(int index) {
return index;
}
#Override
public E get(int index) {
return converter.apply(getSource().get(index));
}
#Override
public int size() {
return getSource().size();
}
#Override
protected void sourceChanged(Change<? extends F> change) {
fireChange(new DelegatingChange(change, this));
}
/**
* An implementation of {#link Change} that delegates all methods to a specified change except {#link #getRemoved()}
*/
private class DelegatingChange extends Change<E> implements DelegatingChangeExcluded<E> {
#Delegate(excludes = DelegatingChangeExcluded.class)
private final Change<? extends F> change;
public DelegatingChange(Change<? extends F> change, MirroringList<E, F> list) {
super(list);
this.change = change;
}
#Override
protected int[] getPermutation() {
return new int[0];
}
#Override
public List<E> getRemoved() {
return change.getRemoved().stream()
.map(converter)
.collect(Collectors.toList());
}
}
/**
* This interface is only used to exclude some methods from delegated methods via Lombok's #{#link Delegate}
* so that the compiler doesn't complain.
*/
#SuppressWarnings("unused")
private interface DelegatingChangeExcluded<E> {
List<E> getRemoved();
ObservableList<? extends E> getList();
List<E> getAddedSubList();
}
}

Neither startEdit or setOnEditCommit getting called

I followed this example mentioned on this link -
UITableView - Better Editing through Binding?
I changed it a bit accordingly
Model class -
public static class TableData {
private String firstName, lastName;
private TableData(String first, String last) {
this.firstName = first;
this.lastName = last;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Custom Cell factory -
public static class TextFieldCellFactory implements Callback<TableColumn<TableData, String>, TableCell<TableData, String>> {
#Override
public TableCell<TableData, String> call(TableColumn<TableData, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<TableData, String> {
private TextField textField;
private StringProperty boundToCurrently = null;
private String newval = "";
public TextFieldCell() {
textField = new TextField();
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
System.out.println("key pressed");
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
// commitEdit(newValue);
System.out.println("" + newValue);
newval = newValue;
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(!newValue){
System.out.println("losing focus" + newval);
//commichange();
commitEdit(textField.getText());
}
}
});
this.setGraphic(textField);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.setText(item);
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
}
setting onEditCommit-
c1.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<TableData, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<TableData, String> t) {
System.out.println("ON edit commit" + t);
((TableData) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
}
);
Problem 1- I want to know what happens when commitEdit() is called. Does it invoke setOnEditCommit ? If it does then why its not invoking setOnEditCoommit
Problem 2 - Why its not going into setOnEditCommit?
Problem 3 I applied startEdit just to check if its enters that field. But that method also is not getting invoked.
Can anyone specify what i am missing here.I don't want a workaround. I need to understand whats the reason behind it
P.S I have removed the binding properties as given in the link.
Your table never enters an editing state (because you never ask it to). Because the cell never has isEditing() return true, the default commitEdit() method becomes a no-op.
You need the TableView to know that it has to start editing a cell when the text field in that cell receives focus. You can do this by modifying the focus listener on the text field:
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
getTableView().edit(getIndex(), getTableColumn());
} else {
commitEdit(textField.getText());
}
}
});

Binding an ObservableList to contents of two other ObservableLists?

If I have two separate ObservableLists, and both are put in a single ObservableList for a TableView... is there a way to create a binding between those two ObservableLists and the aggregated one? I tried to mess around and override the calculate() method for the ObjectBinding, but I don't think this is what I want. Any thoughts on how to tackle this?
UPDATE: Going on a related tangent to show an implication discussed below. This is my ObservableImmutableList implementation that is struggling to work with the checked solution.
package com.nield.utilities.fx;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;
import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import com.google.common.collect.ImmutableList;
public final class ObservableImmutableList<T> implements ObservableList<T> {
private volatile ImmutableList<T> backingList;
private final CopyOnWriteArrayList<ListChangeListener<? super T>> listeners = new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<InvalidationListener> invalidationListeners = new CopyOnWriteArrayList<>();
private final ObjectProperty<ObservableList<T>> property;
private ObservableImmutableList(ImmutableList<T> immutableList) {
this.backingList = immutableList;
this.property = new SimpleObjectProperty<ObservableList<T>>(this);
}
public static <T> ObservableImmutableList<T> of(ImmutableList<T> immutableList) {
return new ObservableImmutableList<T>(immutableList);
}
public void set(ImmutableList<T> immutableList) {
this.property.setValue(this);
final ImmutableList<T> oldList = this.backingList;
final ImmutableList<T> newList = immutableList;
listeners.forEach(l -> l.onChanged(new Change<T>(this) {
private int changeNum = 0;
#Override
public boolean next() {
changeNum++;
return changeNum <= 2 ? true : false;
}
#Override
public boolean wasUpdated() {
return true;
}
#Override
public void reset() {
// TODO Auto-generated method stub
}
#Override
public int getFrom() {
return 0;
}
#Override
public int getTo() {
return changeNum == 1 ? oldList.size() - 1 : newList.size() - 1;
}
#Override
public List<T> getRemoved() {
return changeNum == 1 ? oldList : ImmutableList.of();
}
#Override
public List<T> getAddedSubList() {
return changeNum == 1 ? ImmutableList.of() : newList;
}
#Override
protected int[] getPermutation() {
int[] permutations = new int[changeNum == 1 ? oldList.size() : newList.size()];
for (int i = 0; i < permutations.length; i++) {
permutations[i] = i;
}
return permutations;
}
}));
this.backingList = immutableList;
invalidationListeners.forEach(l -> l.invalidated(this));
}
public ImmutableList<T> get() {
return backingList;
}
public ObjectProperty<ObservableList<T>> asProperty() {
return property;
}
#Override
public int size() {
return backingList.size();
}
#Override
public boolean isEmpty() {
return backingList.isEmpty();
}
#Override
public boolean contains(Object o) {
return backingList.contains(o);
}
#Override
public Iterator<T> iterator() {
return backingList.iterator();
}
#Override
public Object[] toArray() {
return backingList.toArray();
}
#Override
public <B> B[] toArray(B[] a) {
return backingList.toArray(a);
}
#Override #Deprecated
public boolean add(T e) {
return backingList.add(e);
}
#Override #Deprecated
public boolean remove(Object o) {
return backingList.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
return backingList.containsAll(c);
}
#Override #Deprecated
public boolean addAll(Collection<? extends T> c) {
return backingList.addAll(c);
}
#Override #Deprecated
public boolean addAll(int index, Collection<? extends T> c) {
return backingList.addAll(index, c);
}
#Override #Deprecated
public boolean removeAll(Collection<?> c) {
return backingList.removeAll(c);
}
#Override #Deprecated
public boolean retainAll(Collection<?> c) {
return backingList.retainAll(c);
}
#Override #Deprecated
public void clear() {
backingList.clear();
}
#Override
public T get(int index) {
return backingList.get(index);
}
#Override #Deprecated
public T set(int index, T element) {
return backingList.set(index, element);
}
#Override #Deprecated
public void add(int index, T element) {
backingList.add(index, element);
}
#Override #Deprecated
public T remove(int index) {
return backingList.remove(index);
}
#Override
public int indexOf(Object o) {
return backingList.indexOf(o);
}
#Override
public int lastIndexOf(Object o) {
return backingList.lastIndexOf(o);
}
#Override
public ListIterator<T> listIterator() {
return backingList.listIterator();
}
#Override
public ListIterator<T> listIterator(int index) {
return backingList.listIterator(index);
}
#Override
public ImmutableList<T> subList(int fromIndex, int toIndex) {
return backingList.subList(fromIndex, toIndex);
}
#Override
public void addListener(InvalidationListener listener) {
invalidationListeners.add(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
invalidationListeners.remove(listener);
}
#Override
public void addListener(ListChangeListener<? super T> listener) {
listeners.add(listener);
}
#Override
public void removeListener(ListChangeListener<? super T> listener) {
listeners.remove(listener);
}
#Override #Deprecated
public boolean addAll(T... elements) {
return backingList.addAll(ImmutableList.copyOf(elements));
}
#Override #Deprecated
public boolean setAll(T... elements) {
return false;
}
#Override #Deprecated
public boolean setAll(Collection<? extends T> col) {
return false;
}
#Override #Deprecated
public boolean removeAll(T... elements) {
return backingList.removeAll(ImmutableList.copyOf(elements));
}
#Override #Deprecated
public boolean retainAll(T... elements) {
return false;
}
#Override #Deprecated
public void remove(int from, int to) {
}
#Override
public String toString() {
return backingList.toString();
}
}
And here is the static factory to merge two ObservableLists together so far. It works for standard implementations of ObservableList, but it is not working with my ObservableImmutableList implementation.
package com.nield.finance;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.google.common.collect.ImmutableList;
import com.nield.utilities.fx.ObservableImmutableList;
public class ObservableMerge {
static ObservableImmutableList<String> a = ObservableImmutableList.of(ImmutableList.of("ABQ","DAL"));
static ObservableImmutableList<String> b = ObservableImmutableList.of(ImmutableList.of("HOU","PHX"));
static ObservableList<String> aandb = FXCollections.observableArrayList();
static ObservableList<String> merge(ObservableList<String> into, ObservableList<String>... lists) {
final ObservableList<String> list = into;
for (ObservableList<String> l : lists) {
list.addAll(l);
l.addListener((javafx.collections.ListChangeListener.Change<? extends String> c) -> {
while (c.next()) {
if (c.wasAdded()) {
list.addAll(c.getAddedSubList());
}
if (c.wasRemoved()) {
list.removeAll(c.getRemoved());
}
if (c.wasUpdated()) {
list.removeAll(c.getRemoved());
list.addAll(c.getAddedSubList());
}
}
});
}
return list;
}
public static void main(String...args) {
merge(aandb, a, b);
System.out.println(""+aandb);
a.set(ImmutableList.of("LAX", "BUR"));
System.out.println(""+aandb);
}
}
And here is the static factory to merge two ObservableLists (it should work with the ObservableImmutableList too, but its not.
package com.nield.finance;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.google.common.collect.ImmutableList;
import com.nield.utilities.fx.ObservableImmutableList;
public class ObservableMerge {
static ObservableImmutableList<String> a = ObservableImmutableList.of(ImmutableList.of("ABQ","DAL"));
static ObservableImmutableList<String> b = ObservableImmutableList.of(ImmutableList.of("HOU","PHX"));
static ObservableList<String> aandb = FXCollections.observableArrayList();
static ObservableList<String> merge(ObservableList<String> into, ObservableList<String>... lists) {
final ObservableList<String> list = into;
for (ObservableList<String> l : lists) {
list.addAll(l);
l.addListener((javafx.collections.ListChangeListener.Change<? extends String> c) -> {
while (c.next()) {
if (c.wasAdded()) {
list.addAll(c.getAddedSubList());
}
if (c.wasRemoved()) {
list.removeAll(c.getRemoved());
}
if (c.wasUpdated()) {
list.removeAll(c.getRemoved());
list.addAll(c.getAddedSubList());
}
}
});
}
return list;
}
public static void main(String...args) {
merge(aandb, a, b);
System.out.println(""+aandb);
a.set(ImmutableList.of("LAX", "BUR"));
System.out.println(""+aandb);
}
}
This might be a starting point:
public class ObservableMerge {
static ObservableList<String> a = FXCollections.observableArrayList();
static ObservableList<String> b = FXCollections.observableArrayList();
static ObservableList<String> aandb = FXCollections.observableArrayList();
static ObservableList<String> merge(ObservableList<String> into, ObservableList<String>... lists) {
final ObservableList<String> list = into;
for (ObservableList<String> l : lists) {
list.addAll(l);
l.addListener((javafx.collections.ListChangeListener.Change<? extends String> c) -> {
while (c.next()) {
if (c.wasAdded()) {
list.addAll(c.getAddedSubList());
}
if (c.wasRemoved()) {
list.removeAll(c.getRemoved());
}
}
});
}
return list;
}
public static void main(String...args) {
merge(aandb, a, b);
System.out.println(""+aandb);
a.add("Hello");
b.add("Peter");
System.out.println(""+aandb);
}
}
Just thought I'd document this for future readers. RxJavaFX makes push-driven tasks like this MUCH easier.
ObservableList<String> list1 = FXCollections.observableArrayList();
ObservableList<String> list2 = FXCollections.observableArrayList();
ObservableList<String> combinedList = FXCollections.observableArrayList();
Observable.combineLatest(
JavaFxObservable.fromObservableList(list1),
JavaFxObservable.fromObservableList(list2),
(l1,l2) -> {
ArrayList<String> combined = new ArrayList<>();
combined.addAll(l1);
combined.addAll(l2);
return combined;
}).subscribe(combinedList::setAll);
//return unmodifiable version of combinedList
Here's a full working example of this at work:
ObservableList<String> list1 = FXCollections.observableArrayList();
ObservableList<String> list2 = FXCollections.observableArrayList();
ObservableList<String> combinedList = FXCollections.observableArrayList();
Observable.combineLatest(
JavaFxObservable.fromObservableList(list1),
JavaFxObservable.fromObservableList(list2),
(l1,l2) -> {
ArrayList<String> combined = new ArrayList<>();
combined.addAll(l1);
combined.addAll(l2);
return combined;
}).subscribe(combinedList::setAll);
JavaFxObservable.fromObservableList(combinedList).subscribe(System.out::println);
list1.add("Alpha");
list2.add("Beta");
list1.add("Gamma");
list1.remove("Alpha");
list2.add("Delta");
Thread.sleep(10000);
OUTPUT
[]
[Alpha]
[Alpha, Beta]
[Alpha, Gamma, Beta]
[Gamma, Beta]
[Gamma, Beta, Delta]

How to use WeakChangeListener with JavaFx?

I wrote my TableCell implementation based on TableCell. I'm using ChangeListener, but it is not garbage collected. How to use WeakChangeListener in this case. Please see my code. How to switch it to WeakChangeListener.
changeListener = new ChangeListener<Object[]>() {
#Override
public void changed(ObservableValue<? extends Object[]> observable, Object[] oldValue, Object[] newValue) {
if (newValue != null && oldValue != null) {
if (oldValue[21].equals(newValue[21])) {
if (newValue[updateBasedValues] != null) {
if (!newValue[updateBasedValues].equals(oldValue[updateBasedValues])) {
justUpdated = true;
}
}
}
}
}
};
itemProperty().addListener(changeListener);
You need to keep a reference to the weak listener so that it is not garbage collected too soon.
Read this very good discussion on the oracle forum : https://community.oracle.com/thread/2396063
I've written an adapter class "WeakAdapter" that you can use or extend.
Example on how to use this class:
Instead of writing
myBooleanProperty.addListener(new ChangeListener<Boolean> () {...});
use:
weak = new WeakAdapter();
weak.addChangeListener(myBooleanProperty, new ChangeListener<Boolean> (){});
Here is the code of class WeakAdapter:
public class WeakAdapter {
ArrayList<Object> listenerRefs = new ArrayList<>();
public WeakAdapter() {
}
public void dipose() {
listenerRefs.clear();
}
public final <T> void remove(ChangeListener<T> listener) {
listenerRefs.remove(listener);
}
public final <T> void addChangeListener(final ObservableValue observable, ChangeListener<T> listener) {
listenerRefs.add(listener);
observable.addListener(new WeakChangeListener<>(listener));
}
public final <T> WeakListChangeListener<T> addListChangeListener(ListChangeListener<T> listener) {
listenerRefs.add(listener);
return new WeakListChangeListener<>(listener);
}
public void addInvalidationListener(final Observable listened, InvalidationListener listener) {
listenerRefs.add(listener);
listened.addListener(new WeakInvalidationListener(listener));
}
public final void stringBind(final StringProperty propertyToUpdate, final StringExpression expressionToListen) {
ChangeListener<String> listener = new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String name) {
propertyToUpdate.set(name);
}
};
listenerRefs.add(listener);
expressionToListen.addListener(new WeakChangeListener<>(listener));
listener.changed(null, null, expressionToListen.get());
}
public final void booleanBind(final BooleanProperty propertyToUpdate, final BooleanExpression expressionToListen) {
ChangeListener<Boolean> listener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean name) {
propertyToUpdate.set(name);
}
};
listenerRefs.add(listener);
expressionToListen.addListener(new WeakChangeListener<>(listener));
propertyToUpdate.set(expressionToListen.get());
}
}

Resources