java - JavaFx: can data binding (textfield) be used in production mode? -


i use java 8.0.45. have implemented first javafx application (very simple) data binding. however, biding user input-> pojo seems work bugs. i've checked 200 times. entered new values in text fields , after checked model values. same code, same behaviour. works fine (in cases - 80-90%) model value!=textfield value. i've noticed following. data binding text field works,works , @ point of time binding stops working , new values textfield not passed model. nor exceptions. nor warnings. nothing. binding doesn't work.

i have 4 textfiled created via fxml. 2 string model type. 1 integer. 1 bigdecimal. problem happens these fields(sometimes one, several). number fields can have null values, use example propertyobject not integerproperty (people openjfx advised so).

so javafx bug or what? p.s. use felix osgi, weld cdi, , pax - don't know if matters...

my code following:

dto - pojo model

public class task {      private string name;      private integer order;      private bigdecimal weight;      private string comment;      private final propertychangesupport propertychangesupport;      public task() {         this.propertychangesupport = new propertychangesupport(this);     }      public string getname() {         return name;     }      public void setname(string name) {         string pv = this.name ;         this.name = name;         propertychangesupport.firepropertychange("name", pv, name);     }      public integer getorder() {         return order;     }      public void setorder(integer order) {         integer pv = this.order;         this.order = order;         propertychangesupport.firepropertychange("order", pv, this.order);     }      public bigdecimal getweight() {         return weight;     }      public void setweight(bigdecimal weight) {         bigdecimal pv = this.weight;         this.weight = weight;         propertychangesupport.firepropertychange("weight", pv, weight);     }      public string getcomment() {         return comment;     }      public void setcomment(string comment) {         string pv = this.comment;         this.comment = comment;         propertychangesupport.firepropertychange("comment", pv, this.comment);     }      public void addpropertychangelistener(propertychangelistener listener) {         propertychangesupport.addpropertychangelistener(listener);     }  } 

adapter

public class taskadapter {      private stringproperty nameproperty;      private objectproperty<integer> orderproperty;      private objectproperty<bigdecimal> weightproperty;      private stringproperty commentproperty;        public taskadapter(task task) {         try {             nameproperty=new javabeanstringpropertybuilder().bean(task).name("name").build();             orderproperty=new javabeanobjectpropertybuilder<integer>().bean(task).name("order").build();             weightproperty=new javabeanobjectpropertybuilder<bigdecimal>().bean(task).name("weight").build();             commentproperty=new javabeanstringpropertybuilder().bean(task).name("comment").build();         } catch (nosuchmethodexception ex) {             logger.getlogger(this.getclass().getname()).log(level.severe, null, ex);         }     }      public stringproperty getnameproperty() {         return nameproperty;     }      public objectproperty<integer> getorderproperty() {         return orderproperty;     }      public objectproperty<bigdecimal> getweightproperty() {         return weightproperty;     }      public stringproperty getcommentproperty() {         return commentproperty;     }  } 

bigdecimal converter

public class simplebigdecimalstringconverter extends stringconverter<bigdecimal>{      @override     public string tostring(bigdecimal i) {         if (i == null) {             return "" ;         } else {             return i.tostring();         }     }      @override     public bigdecimal fromstring(string string) {         if (string.trim().length() == 0) {             return null ;         } else {             try {                 return new bigdecimal(string);             } catch (numberformatexception nfe) {                 return null ;             }          }     } } 

integerconverter

public class simpleintegerstringconverter extends stringconverter<integer>{      @override     public string tostring(integer i) {         if (i == null) {             return "" ;         } else {             return i.tostring();         }     }      @override     public integer fromstring(string string) {         if (string.trim().length() == 0) {             return null ;         } else {             try {                 return integer.valueof(string);             } catch (numberformatexception nfe) {                 return null ;             }          }     } } 

initializing code

task task=new task(); taskadapter adapter=new taskadapter(task); nametextfield.textproperty().bindbidirectional(adapter.getnameproperty()); ordertextfield.textproperty().bindbidirectional(adapter.getorderproperty(),new simpleintegerstringconverter()); weighttextfield.textproperty().bindbidirectional(adapter.getweightproperty(),new bigdecimalstringconverter()); commenttextfield.textproperty().bindbidirectional(adapter.getcommentproperty()); 

what happening

javafx bindings use weakchangelisteners behind scenes implement binding. means binding can garbage collected if no other references in scope. in code, adapter defined local variable, gets prematurely garbage collected @ arbitrary time when gc runs.

demo

here's demo using code shows issue. has same text fields define, plus 2 buttons. 1 button dumps value of task console, other forces garbage collector run. you'll see binding stops working run gc.

import javafx.application.application; import javafx.geometry.insets; import javafx.geometry.pos; import javafx.scene.scene; import javafx.scene.control.button; import javafx.scene.control.label; import javafx.scene.control.textfield; import javafx.scene.layout.borderpane; import javafx.scene.layout.gridpane; import javafx.scene.layout.hbox; import javafx.stage.stage; import javafx.util.converter.bigdecimalstringconverter;  public class pojobindingexample extends application {      private textfield nametextfield = new textfield();     private textfield ordertextfield = new textfield();     private textfield weighttextfield = new textfield();     private textfield commenttextfield = new textfield();      @override     public void start(stage primarystage) {         task task = new task();         taskadapter adapter = new taskadapter(task);         nametextfield.textproperty().bindbidirectional(adapter.getnameproperty());         ordertextfield.textproperty().bindbidirectional(adapter.getorderproperty(),new simpleintegerstringconverter());         weighttextfield.textproperty().bindbidirectional(adapter.getweightproperty(),new bigdecimalstringconverter());         commenttextfield.textproperty().bindbidirectional(adapter.getcommentproperty());          gridpane grid = new gridpane();         grid.addrow(0, new label("name:"), nametextfield);         grid.addrow(1, new label("order:"), ordertextfield);         grid.addrow(2, new label("weight:"), weighttextfield);         grid.addrow(3, new label("comment:"), commenttextfield);          button showbutton = new button("show task");         showbutton.setonaction(e -> {             system.out.println(task.getname());             system.out.println(task.getorder());             system.out.println(task.getweight());             system.out.println(task.getcomment());             system.out.println();         });          button gcbutton = new button("run gc");         gcbutton.setonaction(e -> system.gc());          hbox buttons = new hbox(10, showbutton, gcbutton);          borderpane.setalignment(grid, pos.center);         borderpane.setalignment(buttons, pos.center);         borderpane.setmargin(grid, new insets(10));         borderpane.setmargin(buttons, new insets(10));          borderpane root = new borderpane(grid, null, null, buttons, null);          scene scene = new scene(root);         primarystage.setscene(scene);         primarystage.show();     }      public static void main(string[] args) {         launch(args);     } } 

fix

to fix problem, need ensure reference taskadapter persists long need it. in above code, if move reference taskadapter instance field, work required:

public class pojobindingexample extends application {      private textfield nametextfield = new textfield();     private textfield ordertextfield = new textfield();     private textfield weighttextfield = new textfield();     private textfield commenttextfield = new textfield();     private taskadapter adapter;      @override     public void start(stage primarystage) {         task task = new task();         adapter = new taskadapter(task);          // ... etc     } } 

you might interested in reading tomas mikula's blog, though don't think can use library directly implement binding pojo.


Comments

Popular posts from this blog

facebook - android ACTION_SEND to share with specific application only -

python - Creating a new virtualenv gives a permissions error -

javascript - cocos2d-js draw circle not instantly -