java - Bind two Spinner controls into a TableView in JavaFX -
how bind 2 spinner controls tableview ? according sreenshot below, : cola = colb / 2 (and colb = cola x 2...) :
here snippet (deliberately simple) used expose problem :
testapp.java
public class testapp extends application { @override public void start(stage stage) throws exception { final tableview<mybean> tableview = new tableview<>(); final tablecolumn<mybean, integer> cola = new tablecolumn<>("col a"); final tablecolumn<mybean, integer> colb = new tablecolumn<>("col b"); cola.setcellfactory(col -> new spinnercell<mybean, integer>()); cola.setcellvaluefactory(new propertyvaluefactory<mybean, integer>("vala")); colb.setcellfactory(col -> new spinnercell<mybean, integer>()); colb.setcellvaluefactory(new propertyvaluefactory<mybean, integer>("valb")); tableview.setcolumnresizepolicy(tableview.constrained_resize_policy); tableview.setitems(fxcollections.observablearraylist(new mybean(1, 2))); tableview.getcolumns().addall(cola, colb); stage.setscene(new scene(new vbox(tableview), 500, 300)); stage.show(); } public static void main(string[] args) { application.launch(); } }
spinnercell.java
public class spinnercell<s, t> extends tablecell<s, t> { private spinner<integer> spinner; private observablevalue<t> ov; public spinnercell() { this.spinner = new spinner<integer>(0, 100, 1); setalignment(pos.center); } @override protected void updateitem(integer item, boolean empty) { super.updateitem(item, empty); if (empty) { settext(null); setgraphic(null); } else { settext(null); setgraphic(this.spinner); if(this.ov instanceof integerproperty) { this.spinner.getvaluefactory().valueproperty().unbindbidirectional(((integerproperty) this.ov).asobject()); } this.ov = gettablecolumn().getcellobservablevalue(getindex()); if(this.ov instanceof integerproperty) { this.spinner.getvaluefactory().valueproperty().bindbidirectional(((integerproperty) this.ov).asobject()); } } } }
mybean.java
public class mybean { private integerproperty vala, valb; public mybean(int vala, int valb) { this.vala = new simpleintegerproperty(this, "vala", vala); this.valb = new simpleintegerproperty(this, "valb", valb); } public integerproperty valaproperty() { return this.vala; } public void setvala(int vala) { this.vala.set(vala); } public int getvala() { return vala.get(); } public integerproperty valbproperty() { return this.valb; } public void setvalb(int valb) { this.valb.set(valb); } public int getvalb() { return valb.get(); } }
here's example of using extended bidirectional binding support in mybean:
public static class mybean { private integerproperty vala; private integerproperty valb; public mybean(int vala) { this.vala = new simpleintegerproperty(this, "vala", vala); this.valb = new simpleintegerproperty(this, "valb", 0); updateb(this.vala, null, this.vala.get()); bidirectionalbinding.<number, number>bindbidirectional( this.vala, this.valb, this::updateb, this::updatea); } protected void updateb(observablevalue<? extends number> source, number old, number value) { setvalb(value.intvalue() * 2); } protected void updatea(observablevalue<? extends number> source, number old, number value) { setvala(value.intvalue() / 2); } ... // same in op's code }
plus in spinnercell, bind bean property directly (vs. asobject wrapper) - there's typing issue don't understand entirely [update, see below] (me , generics never become friends ;-) stands in way of successful bidi-binding:
public static class spinnercell<s, t extends number> extends tablecell<s, t> { private spinner<t> spinner; private observablevalue<t> ov; public spinnercell() { this(1); } public spinnercell(int step) { this.spinner = new spinner<>(0, 100, step); setalignment(pos.center); } @override protected void updateitem(t item, boolean empty) { super.updateitem(item, empty); if (empty) { settext(null); setgraphic(null); } else { settext(null); setgraphic(this.spinner); if(this.ov instanceof property) { this.spinner.getvaluefactory().valueproperty().unbindbidirectional(((property) this.ov)); } this.ov = gettablecolumn().getcellobservablevalue(getindex()); if(this.ov instanceof property) { this.spinner.getvaluefactory().valueproperty().bindbidirectional(((property) this.ov)); } } } }
update (in understanding problem .asobject)
the problem not typing such, (struck again!) weak listener registration in bidi-binding:
// spinner type spinner<integer> spinner; // value type (in valuefactory): objectproperty<integer> valueproperty; // value type in bean: integerproperty valxproperty; // bindeable spinner's value, needs wrapped // objectproperty<integer> // intuitively ... wrong! valueproperty.bindbidirectional(bean.valxproperty().asobject());
the wrapper created on fly local reference can (and is) garbage collected containing method left ... these weak listening contexts, none (? @ least none i'm aware of) of alternatives satisfying:
- relax on typing of spinner: using number (vs.integer) doesn't require wrapper because
interproperty instanceof objectproperty<number>
- keep strong reference wrapper somewhere
Comments
Post a Comment