java - Javafx where to bind labels to StringProperty -
i've been struggling several days, i've read threads, mvc, bindings, interfaces , many interesting things cant put them in appropriate way make work.
i want list files in mi c:\ , display them on changing label is:
exception in thread "thread-4" java.lang.illegalstateexception: not on fx application thread; currentthread = thread-4
this fxml:
<?import javafx.scene.control.button?> <?import javafx.scene.control.label?> <?import javafx.scene.layout.anchorpane?> <?import javafx.scene.layout.columnconstraints?> <?import javafx.scene.layout.gridpane?> <?import javafx.scene.layout.rowconstraints?> <gridpane alignment="center" hgap="10" prefheight="200.0" prefwidth="401.0" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="sample.controller"> <columnconstraints> <columnconstraints /> </columnconstraints> <rowconstraints> <rowconstraints /> </rowconstraints> <children> <anchorpane prefheight="200.0" prefwidth="368.0"> <children> <button fx:id="start" layoutx="159.0" layouty="35.0" mnemonicparsing="false" onaction="#displayfiles" text="start" /> <label fx:id="filelabel" layoutx="20.0" layouty="100.0" prefheight="21.0" prefwidth="329.0" text="this label must change on iteration" /> </children> </anchorpane> </children> </gridpane>
my main:
import javafx.application.application; import javafx.fxml.fxml; import javafx.fxml.fxmlloader; import javafx.scene.parent; import javafx.scene.scene; import javafx.stage.stage; public class main extends application { @override public void start(stage primarystage) throws exception{ parent root = fxmlloader.load(getclass().getresource("sample.fxml")); primarystage.settitle("dummy app"); primarystage.setscene(new scene(root)); primarystage.setresizable(false); primarystage.show(); } public static void main(string[] args) { launch(args); } }
my controller:
import javafx.event.actionevent; import javafx.fxml.fxml; import javafx.scene.control.button; import javafx.scene.control.label; public class controller { @fxml button start; @fxml label filelabel; @fxml void displayfiles(actionevent event) throws exception{ model model = new model(); //binding filelabel.textproperty().bind(model.status); thread thread = new thread(model); thread.start(); } }
and model:
import javafx.beans.property.simplestringproperty; import javafx.beans.property.stringproperty; import java.io.file; /** * created r00715649 on 16-nov-16. */ public class model implements runnable { file rootdirectory = new file("c:/"); stringproperty status = new simplestringproperty("starting scan..."); @override public void run() { try{ file[] filelist = rootdirectory.listfiles(); (file f:filelist){ processdirectory(f); } }catch (exception e){ } } void processdirectory (file directory){ if (directory.isdirectory()){ file[] filelist = directory.listfiles(); (file f:filelist){ processdirectory(f); } }else{ system.out.println(directory.getabsolutepath()); status.set(directory.getabsolutepath()); } } }
since text of label bound model's status, changing model's status results in change in ui (the text of label change). consequently, can change model's status on fx application thread.
you can schedule code run on fx application thread using platform.runlater(...)
. can either in model directly:
void processdirectory (file directory){ if (directory.isdirectory()){ file[] filelist = directory.listfiles(); (file f:filelist){ processdirectory(f); } }else{ system.out.println(directory.getabsolutepath()); platform.runlater(() -> status.set(directory.getabsolutepath())); } }
or can register listener model's status (instead of binding), , delegate fx application thread there:
@fxml void displayfiles(actionevent event) throws exception{ model model = new model(); changelistener<string> listener = (obs, oldstatus, newstatus) -> filelabel.settext(newstatus); model.status.addlistener(listener); thread thread = new thread(model); thread.start(); }
in latter solution, want remove listener when thread finishes (which needs additional work), otherwise model cannot garbage collected while label still displayed. end, consider using task
:
@fxml void displayfiles(actionevent event) throws exception{ model model = new model(); task<void> task = new task<void>() { @override public void call() { model.status.addlistener((obs, oldstatus, newstatus) -> updatemessage(newstatus)); model.run(); return null ; } }; filelabel.textproperty().bind(task.messageproperty()); task.setonsucceeded(e -> filelabel.textproperty().unbind()); new thread(task).start(); }
Comments
Post a Comment