{"id":1810,"date":"2015-01-26T22:52:59","date_gmt":"2015-01-26T21:52:59","guid":{"rendered":"http:\/\/www.jensd.de\/wordpress\/?p=1810"},"modified":"2015-01-27T08:41:15","modified_gmt":"2015-01-27T07:41:15","slug":"how-to-allow-you-users-to-easily-customize-their-view","status":"publish","type":"post","link":"https:\/\/www.jensd.de\/wordpress\/?p=1810","title":{"rendered":"How to allow users to customize the UI"},"content":{"rendered":"<p><strong>Idea<\/strong><\/p>\n<p>Take advantage of the declarative design pattern of JavafX\/FXML and allow users to customize a certain view without any coding just by opening it with e.g. SceneBuilder to re-arrange the layout or add new controls or even change the style according to the users needs.<\/p>\n<p>The FXML file + CSS can be basically be placed whereever they are reachable via a URL.<br \/>\nThe user must only know the interface\/methods of the assigned controller class inside the FXML.<\/p>\n<p><iframe loading=\"lazy\" title=\"Custom FXML: Dynamically load customized JavaFX UI\" width=\"900\" height=\"506\" src=\"https:\/\/www.youtube.com\/embed\/ySEWwJDRSj0?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe><\/p>\n<p><strong>RemoteController<\/strong><\/p>\n<p>Assuming this simple demo controller class provides methods to remotely control devices and to send MQTT messages, a user is able to customize his own remote control.<\/p>\n<p>[java]<br \/>\npublic class RemoteController{<\/p>\n<p>    @FXML<br \/>\n    public void onTest(){<br \/>\n        Alert alert = new Alert(Alert.AlertType.INFORMATION);<br \/>\n        alert.setContentText(&#8220;&#8221;);<br \/>\n        alert.setHeaderText(&#8220;WORKS!&#8221;);<br \/>\n        alert.show();<br \/>\n    }<\/p>\n<p>    public void onTest(String value){<br \/>\n        Alert alert = new Alert(Alert.AlertType.INFORMATION);<br \/>\n        alert.setHeaderText(&#8220;WORKS!&#8221;);<br \/>\n        alert.setContentText(value);<br \/>\n        alert.show();<br \/>\n    }<\/p>\n<p>    public void onSwitch(String houseCode, int groudId, int deviceId, String command){<br \/>\n        Alert alert = new Alert(Alert.AlertType.INFORMATION);<br \/>\n        alert.setHeaderText(&#8220;Switch!&#8221;);<br \/>\n        alert.setContentText(String.format(&#8220;Command: send %s %d %d %s&#8221;, houseCode, groudId, deviceId, command));<br \/>\n        alert.show();<br \/>\n    }<br \/>\n}<br \/>\n[\/java]<\/p>\n<p><strong>remote.fxml and remote.css<\/strong><\/p>\n<p>Note the referenced <code><strong>de.jensd.shichimifx.demo.ext.RemoteController<\/strong><\/code> and <code><strong>remote.css<\/strong><\/code>.<br \/>\nSo basically controller actions can be called via<\/p>\n<pre>\r\nonAction=&quot;#onTest&quot;.\r\n<\/pre>\n<p><strong>Nice:<\/strong><br \/>\nIf you add<\/p>\n<pre>&lt;?language javascript?&gt;<\/pre>\n<p>to FXML, it&#8217;s also possible to pass parameters by a JavaScript call via the <code><strong>controller<\/strong><\/code>-instance.<\/p>\n<pre>\r\nonAction=controller.onTest(&#039;OFF&#039;)\r\nonAction=controller.onSwitch(&#039;a&#039;,1,1,&#039;ON&#039;)\r\n<\/pre>\n<p>Unfortunately I can&#8217;t find more documentation about this feature than -> <a href=\"http:\/\/docs.oracle.com\/javafx\/2\/api\/javafx\/fxml\/doc-files\/introduction_to_fxml.html#scripting\" title=\"http:\/\/docs.oracle.com\/javafx\/2\/api\/javafx\/fxml\/doc-files\/introduction_to_fxml.html#scripting\" target=\"_blank\">this<\/a>, but somehow it magically it works ;-).  Its even possible to pass different types of parameters.<\/p>\n<p>[xml]<br \/>\n<?xml version=\"1.0\" encoding=\"UTF-8\"?><\/p>\n<p><?language javascript?><br \/>\n<?import javafx.geometry.*?><br \/>\n<?import java.lang.*?><br \/>\n<?import java.net.*?><br \/>\n<?import java.util.*?><br \/>\n<?import javafx.scene.*?><br \/>\n<?import javafx.scene.control.*?><br \/>\n<?import javafx.scene.layout.*?><\/p>\n<p><VBox alignment=\"TOP_CENTER\" prefHeight=\"400.0\" prefWidth=\"600.0\" spacing=\"20.0\" styleClass=\"main-pane\" stylesheets=\"@remote.css\" xmlns=\"http:\/\/javafx.com\/javafx\/8\" xmlns:fx=\"http:\/\/javafx.com\/fxml\/1\" fx:controller=\"de.jensd.shichimifx.demo.ext.RemoteController\"><br \/>\n   <children><br \/>\n      <Label styleClass=\"title-label\" text=\"Universal Remote\" \/><br \/>\n      <HBox alignment=\"CENTER_RIGHT\" spacing=\"20.0\"><br \/>\n         <children><br \/>\n            <Label layoutX=\"228.0\" layoutY=\"96.0\" styleClass=\"sub-title-label\" text=\"Light Frontdoor\" \/><br \/>\n            <Button layoutX=\"43.0\" layoutY=\"86.0\" mnemonicParsing=\"false\" onAction=\"#onTest\" prefWidth=\"150.0\" styleClass=\"button-on\" text=\"ON\" \/><br \/>\n            <Button layoutX=\"411.0\" layoutY=\"86.0\" mnemonicParsing=\"false\" onAction=\"#onTest\" prefWidth=\"150.0\" styleClass=\"button-off\" text=\"OFF\" \/><br \/>\n         <\/children>\n         <padding>\n            <Insets left=\"10.0\" right=\"10.0\" \/>\n         <\/padding>\n      <\/HBox><br \/>\n      <HBox alignment=\"CENTER_RIGHT\" spacing=\"20.0\"><br \/>\n         <children><br \/>\n            <Label layoutX=\"228.0\" layoutY=\"96.0\" styleClass=\"sub-title-label\" text=\"Light Garden\" \/><br \/>\n            <Button layoutX=\"43.0\" layoutY=\"86.0\" mnemonicParsing=\"false\" onAction=\"controller.onTest('ON')\" prefWidth=\"150.0\" styleClass=\"button-on\" text=\"ON\" \/><br \/>\n            <Button layoutX=\"411.0\" layoutY=\"86.0\" mnemonicParsing=\"false\" onAction=\"controller.onTest('OFF')\" prefWidth=\"150.0\" styleClass=\"button-off\" text=\"OFF\" \/><br \/>\n         <\/children>\n         <padding>\n            <Insets left=\"10.0\" right=\"10.0\" \/>\n         <\/padding>\n      <\/HBox><br \/>\n      <HBox alignment=\"CENTER_RIGHT\" spacing=\"20.0\"><br \/>\n         <children><br \/>\n            <Label layoutX=\"228.0\" layoutY=\"96.0\" styleClass=\"sub-title-label\" text=\"Light Garden\" \/><br \/>\n            <Button layoutX=\"43.0\" layoutY=\"86.0\" mnemonicParsing=\"false\" onAction=\"controller.onSwitch('a', 1,1,'ON')\" prefWidth=\"150.0\" styleClass=\"button-on\" text=\"ON\" \/><br \/>\n            <Button layoutX=\"411.0\" layoutY=\"86.0\" mnemonicParsing=\"false\" onAction=\"controller.onTest('OFF')\" prefWidth=\"150.0\" styleClass=\"button-off\" text=\"OFF\" \/><br \/>\n         <\/children>\n         <padding>\n            <Insets left=\"10.0\" right=\"10.0\" \/>\n         <\/padding>\n      <\/HBox><br \/>\n   <\/children>\n   <padding>\n      <Insets bottom=\"20.0\" left=\"20.0\" right=\"20.0\" top=\"20.0\" \/>\n   <\/padding>\n<\/VBox><br \/>\n[\/xml]<\/p>\n<p>Based on this example a user is able to simple open the FXMl with SceneBuilder and to add new Button calling the controller.onSwitch() method to control different\/new devices installed for home automation.<\/p>\n<p><strong>FxmlUtils<\/strong><br \/>\nThe next release of <a href=\"https:\/\/bitbucket.org\/Jerady\/shichimifx\" title=\"https:\/\/bitbucket.org\/Jerady\/shichimifx\" target=\"_blank\">ShichimiFX<\/a> will contain a new Utilily class to load FXML as shown in the <code><strong>ExternalFXMLDemoController<\/strong><\/code>. <strong>Note<\/strong> that the loaded Pane is added to the center of the <code><strong>externalPane<\/strong><\/code> (BorderPane) of the Demo-Application via <code><strong>onLoadExternalFxml()<\/strong><\/code>:<\/p>\n<p>[java]<br \/>\npublic class ExternalFXMLDemoController {<\/p>\n<p>    @FXML<br \/>\n    private ResourceBundle resources;<\/p>\n<p>    @FXML<br \/>\n    private BorderPane externalPane;<\/p>\n<p>    @FXML<br \/>\n    private TextField fxmlFileNameTextField;<\/p>\n<p>    @FXML<br \/>\n    private Button chooseFxmlFileButton;<\/p>\n<p>    @FXML<br \/>\n    private Button loadFxmlFileButton;<\/p>\n<p>    private StringProperty fxmlFileName;<\/p>\n<p>    public void initialize() {<br \/>\n        fxmlFileNameTextField.textProperty().bindBidirectional(fxmlFileNameProperty());<br \/>\n        loadFxmlFileButton.disableProperty().bind(fxmlFileNameProperty().isEmpty());<br \/>\n    }<\/p>\n<p>    public StringProperty fxmlFileNameProperty() {<br \/>\n        if (fxmlFileName == null) {<br \/>\n            fxmlFileName = new SimpleStringProperty(&#8220;&#8221;);<br \/>\n        }<br \/>\n        return fxmlFileName;<br \/>\n    }<\/p>\n<p>    public String getFxmlFileName() {<br \/>\n        return fxmlFileNameProperty().getValue();<br \/>\n    }<\/p>\n<p>    public void setFxmlFileName(String fxmlFileName) {<br \/>\n        this.fxmlFileNameProperty().setValue(fxmlFileName);<br \/>\n    }<\/p>\n<p>    @FXML<br \/>\n    public void chooseFxmlFile() {<br \/>\n        FileChooser chooser = new FileChooser();<br \/>\n        chooser.setTitle(&#8220;Choose FXML file to load&#8221;);<br \/>\n        if (getFxmlFileName().isEmpty()) {<br \/>\n            chooser.setInitialDirectory(new File(System.getProperty(&#8220;user.home&#8221;)));<br \/>\n        } else {<br \/>\n            chooser.setInitialDirectory(new File(getFxmlFileName()).getParentFile());<br \/>\n        }<\/p>\n<p>        File file = chooser.showOpenDialog(chooseFxmlFileButton.getScene().getWindow());<br \/>\n        if (file != null) {<br \/>\n            setFxmlFileName(file.getAbsolutePath());<br \/>\n        }<br \/>\n    }<\/p>\n<p>    @FXML<br \/>\n    public void onLoadExternalFxml() {<br \/>\n        try {<br \/>\n            Optional<URL> url = FxmlUtils.getFxmlUrl(Paths.get(getFxmlFileName()));<br \/>\n            if (url.isPresent()) {<br \/>\n                Pane pane = FxmlUtils.loadFxmlPane(url.get(), resources);<br \/>\n                externalPane.setCenter(pane);<br \/>\n            } else {<br \/>\n                Alert alert = new Alert(Alert.AlertType.WARNING);<br \/>\n                alert.setContentText(getFxmlFileName() + &#8221; could not be found!&#8221;);<br \/>\n                alert.show();<br \/>\n            }<br \/>\n        } catch (IOException ex) {<br \/>\n            Dialogs.create().showException(ex);<br \/>\n        }<br \/>\n    }<br \/>\n}<br \/>\n[\/java]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Idea Take advantage of the declarative design pattern of JavafX\/FXML and allow users to customize a certain view without any coding just by opening it with e.g. SceneBuilder to re-arrange the layout or add new controls or even change the style according to the users needs. The FXML file + CSS can be basically be&hellip; <span class=\"clear\"><\/span><a href=\"https:\/\/www.jensd.de\/wordpress\/?p=1810\" class=\"more-link read-more\" rel=\"bookmark\">Continue Reading <span class=\"screen-reader-text\">How to allow users to customize the UI<\/span><i class=\"fa fa-arrow-right\"><\/i><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"footnotes":"","_jetpack_memberships_contains_paid_content":false,"jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[51,32,59,4,58,57,49],"tags":[107,93,65,76,111,64,105],"jetpack_publicize_connections":[],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p38FCL-tc","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/1810"}],"collection":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1810"}],"version-history":[{"count":22,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/1810\/revisions"}],"predecessor-version":[{"id":1832,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/1810\/revisions\/1832"}],"wp:attachment":[{"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1810"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1810"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jensd.de\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1810"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}