java - InvalidationListener only executed in debug mode with breakpoint -
i have problem invalidationlistener. set listener on simplestringproperty. called first change of simplestringproperty. entered debug-mode , made break-point on line calls simplestringproperty::set , started working until removed break-point again.
i made short executable example program simulates modification of simplestringproperty timer. may run program 1 time without break points , 1 time having breakpoint @ line: property.set(value);
import javafx.animation.keyframe; import javafx.animation.timeline; import javafx.application.application; import javafx.beans.property.simplestringproperty; import javafx.scene.scene; import javafx.scene.layout.borderpane; import javafx.stage.stage; import javafx.util.duration; public class main extends application { private simplestringproperty property; private int counter; @override public void start(stage stage) { // open window avoid termination stage.setwidth(800); stage.setheight(600); borderpane pane = new borderpane(); stage.setscene(new scene(pane)); stage.show(); // create simpleobjectproperty property = new simplestringproperty(); property.addlistener(observable -> system.out.println("new value is: " + counter) ); counter = 0; // create timer change 'property' every second timeline timeline = new timeline(); keyframe keyframe = new keyframe(duration.seconds(2), event ->{ string value = "" + ++counter; system.out.println("set property to: " + value); property.set(value); }); timeline.getkeyframes().add(keyframe); timeline.setcyclecount(timeline.indefinite); timeline.playfromstart(); } public static void main(string[] args) { launch(args); } }
output on machine (linux mint 16.04 64bit, oracle-java 1.8.0_111):
set property to: 1 new value is: 1 set property to: 2 set property to: 3 set property to: 4 ...
please explain me:
- why listener not called on every change?
- why listener called, when set break-point?
- what should make working without break-points?
an observable value has 2 different states changes can trigger listeners. there value, , there state of whether or not valid.
in general, value of observable value may computed, rather stored in field. once value has been "realized" (my terminology), being computed if computed, or being retrieved if stored in field, observable value in "valid" state. if value changes (or may have changed), observable value becomes "invalid", indicating may need recomputed or looked again.
the invalidation listener triggered when observable value transitions valid state invalid one. in code, first time call
property.set(value);
the property transitions invalid state (because retrieved value, if any, not current value).
since don't ever call property.get()
(or property.getvalue()
), property never gets validated. consequently, next time call property.set(value)
, property not transition invalid state (it in state), , listener not fired.
if replace listener code with
property.addlistener(observable -> system.out.println("new value is: " + property.get()) );
this listener cause property become valid again, , listener fired each time.
the real issue using wrong kind of listener here. if want perform action every time value changes, use changelistener
, not invalidationlistener
:
property.addlistener((observable, oldvalue, newvalue) -> system.out.println("new value is: " + newvalue) );
the observation running in debug mode breakpoint causes invalidation listener invoked every time interesting one. i'm guessing bit, suspect happening when hit breakpoint, debugger showing current values of variables. inevitably involves calling getvalue()
on property (as part of tostring()
implementation, perhaps), , property becomes validated.
you unlikely explicitly use invalidationlistener
often. main use in bindings. consider following example:
doubleproperty x = new simpledoubleproperty(3); doubleproperty y = new simpledoubleproperty(4); doublebinding hyp = new doublebinding() { { bind(x); bind(y); } @override protected double computevalue() { system.out.println("computing distance"); return math.sqrt(x.get()*x.get() + y.get()*y.get()); } }; label hyplabel = new label(); hyplabel.textproperty().bind(hyp.asstring("hypotenuse: %f"));
the call bind(x)
in binding implementation means: when x
becomes invalid, consider binding invalid. y
. of course, implementation of bind
uses invalidationlistener
s under hood.
the point here computation of hyp
's value pretty expensive. if make multiple changes x
or y
, hyp
becomes invalid , needs recomputed when need again. since text property of label bound hyp
, means label's text becomes invalid. however, need new value when label repainted in rendering pulse; overkill compute value every change of x
, y
.
Thanks for the post, I am techno savvy. I believe you hit the nail right on the head. I am highly impressed with your blog.
ReplyDeleteIt is very nicely explained. Your article adds best knowledge to our Java Online Training from India.
or learn thru Java Online Training from India Students.