package org.codefx.libfx.listener.handle;
import javafx.beans.Observable;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
/**
* Demonstrates how to create and use {@link ListenerHandle}s.
*/
@SuppressWarnings("static-method")
public class ListenerHandleDemo {
// #begin CONSTRUCTION & MAIN
/**
* Creates a new demo.
*/
private ListenerHandleDemo() {
// nothing to do
}
/**
* Runs this demo.
*
* @param args
* command line arguments (will not be used)
*/
public static void main(String[] args) {
ListenerHandleDemo demo = new ListenerHandleDemo();
demo.createCommonListenerHandle();
demo.createCustomListenerHandle();
demo.attachAndDetach();
}
// #end CONSTRUCTION & MAIN
// #begin DEMOS
// construction
/**
* Demonstrates how to simply create a handle for a given observable and listener.
*/
private void createCommonListenerHandle() {
Property<String> property = new SimpleStringProperty();
ChangeListener<String> listener = (obs, oldValue, newValue) -> { /* do nothing for this demo */};
// create the handle; this one is initially attached, i.e. the listener is added to the property
ListenerHandle handle = ListenerHandles.createAttached(property, listener);
// the handle can be used to easily detach and reattach the listener
handle.detach();
handle.attach();
// create a detached handle where the listener was not yet added to the property
handle = ListenerHandles.createDetached(property, listener);
// this one needs to be attached before the listener is executed on changes
handle.attach();
}
/**
* Demonstrates how a listener handle can be created for custom observable implementations with
* {@link ListenerHandleBuilder}.
*/
private void createCustomListenerHandle() {
MyCustomObservable customObservable = new MyCustomObservable();
MyCustomListener customListener = new MyCustomListener();
// use 'ListenerHandles' to get a 'ListenerHandleBuilder' which can be used to create a handle for this
// observable and listener
ListenerHandles
.createFor(customObservable, customListener)
.onAttach((observable, listener) -> observable.addListener(listener))
.onDetach((observable, listener) -> observable.removeListener(listener))
.buildAttached();
}
// attach & detach
/**
* Demonstrates how to add and remove a listener with a {@link ListenerHandle} and compares this to the normal
* approach.
*/
private void attachAndDetach() {
Property<String> observedProperty = new SimpleStringProperty("initial value");
// usually a listener is directly added to the property;
// but if the listener has to be removed later, the reference needs to be stored explicitly
ChangeListener<Object> changePrintingListener = (obs, oldValue, newValue) ->
System.out.println("[LISTENER] Value changed from \"" + oldValue + "\" to \"" + newValue + "\".");
observedProperty.addListener(changePrintingListener);
// this is the alternative with a 'ListenerHandle'
ListenerHandle newValuePrinter = ListenerHandles.createAttached(observedProperty,
(obs, oldValue, newValue) -> System.out.println("[HANDLE] New value: \"" + newValue + "\""));
// now lets change the value to see how it works
observedProperty.setValue("new value");
observedProperty.setValue("even newer value");
// removing a listener needs references to both the observable and the listener;
// depending on the situation this might not be feasible
observedProperty.removeListener(changePrintingListener);
// with a handle, the listener can be removed without giving the caller the possibility tp interact with
// the observable or the listener; it is also a little more readable
newValuePrinter.detach();
// some unobserved changes...
observedProperty.setValue("you won't see this on the console");
observedProperty.setValue("nor this");
// the same as above goes for adding the listener
observedProperty.addListener(changePrintingListener);
newValuePrinter.attach();
// now some more changes
observedProperty.setValue("but you will see this");
observedProperty.setValue("and this");
}
// #end DEMOS
// #begin NESTED CLASSES
/**
* Represents a custom observable instance. Note that it is not necessary to implement {@link Observable} (or any
* other interface) in order to use this class with the {@link ListenerHandleBuilder}.
*/
private static class MyCustomObservable {
@SuppressWarnings({ "unused" })
public void addListener(MyCustomListener listener) {
// do nothing - just for demo
}
@SuppressWarnings({ "unused" })
public void removeListener(MyCustomListener listener) {
// do nothing - just for demo
}
}
/**
* Represents a listener for a custom observable instance. Note that it is not necessary to implement
* {@link ChangeListener} (or any other interface) in order to use this class with the {@link ListenerHandleBuilder}
* .
*/
private static class MyCustomListener {
// has no members - just for demo
}
// #end NESTED CLASSES
}