Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><strong>Update:</strong> For an up-to-date tutorial, please, consult <a href="http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm" rel="nofollow noreferrer">the official documentation</a>. There was a lot of <a href="http://docs.oracle.com/javafx/2/fxml_get_started/whats_new2.htm" rel="nofollow noreferrer">new stuff</a> that was added in 2.2. Also, the <a href="http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html" rel="nofollow noreferrer">Introduction to FXML</a> covers pretty much everything you need to know about FXML. Finally, Hendrik Ebbers made an extremely helpful <a href="http://www.guigarage.com/2012/11/custom-ui-controls-with-javafx-part-1/" rel="nofollow noreferrer">blog post</a> about custom UI controls.</p> <hr> <p>After a few days of looking around the <a href="http://docs.oracle.com/javafx/2.0/api/" rel="nofollow noreferrer">API</a> and reading through some docs (<a href="http://docs.oracle.com/javafx/2.0/api/javafx/fxml/doc-files/introduction_to_fxml.html" rel="nofollow noreferrer">Intro to FXML</a>, <a href="http://docs.oracle.com/javafx/2.0/fxml_get_started/jfxpub-fxml_get_started.htm" rel="nofollow noreferrer">Getting started with FXML</a> <a href="http://docs.oracle.com/javafx/2.0/binding/jfxpub-binding.htm" rel="nofollow noreferrer">Property binding</a>, <a href="http://fxexperience.com/2011/10/fxml-why-it-rocks-and-the-next-phase/" rel="nofollow noreferrer">Future of FXML</a>) I've come up with a fairly sensible solution. The least straight-forward piece of information I learned from this little experiment was that the instance of a controller (declared with fx:controller in FXML) is held by the <a href="http://docs.oracle.com/javafx/2.0/api/javafx/fxml/FXMLLoader.html" rel="nofollow noreferrer">FXMLLoader</a> that loaded the FXML file... Worst of all, this important fact is only mentioned in <a href="http://docs.oracle.com/javafx/2.0/api/javafx/fxml/doc-files/introduction_to_fxml.html#controllers" rel="nofollow noreferrer">one place</a> in all the docs I saw: </p> <blockquote> <p>a controller is generally only visible to the FXML loader that creates it</p> </blockquote> <p>So, remember, in order to programmatically (from Java code) obtain a reference to the instance of a controller that was declared in FXML with <code>fx:controller</code> use <a href="http://docs.oracle.com/javafx/2.0/api/javafx/fxml/FXMLLoader.html#getController%28%29" rel="nofollow noreferrer">FXMLLoader.getController()</a> (refer to the implementation of the ChoiceCell class below for a complete example).</p> <p>Another thing to note is that <a href="http://docs.oracle.com/javafx/2.0/api/javafx/beans/property/Property.html#bindBidirectional%28javafx.beans.property.Property%29" rel="nofollow noreferrer">Property.bindBiderctional()</a> will set the value of the calling property to the value of the property passed in as the argument. Given two boolean properties <code>target</code> (originally set to <code>false</code>) and <code>source</code> (initially set to <code>true</code>) calling <code>target.bindBidirectional(source)</code> will set the value of <code>target</code> to <code>true</code>. Obviously, any subsequent changes to either property will change the other property's value (<code>target.set(false)</code> will cause the value of <code>source</code> to be set to <code>false</code>):</p> <pre><code>BooleanProperty target = new SimpleBooleanProperty();//value is false BooleanProperty source = new SimpleBooleanProperty(true);//value is true target.bindBidirectional(source);//target.get() will now return true target.set(false);//both values are now false source.set(true);//both values are now true </code></pre> <p>Anyway, here is the complete code that demonstrates how FXML and Java can work together (as well as a few other useful things)</p> <p>Package structure:</p> <pre><code>com.example.javafx.choice ChoiceCell.java ChoiceController.java ChoiceModel.java ChoiceView.fxml com.example.javafx.mvc FxmlMvcPatternDemo.java MainController.java MainView.fxml MainView.properties </code></pre> <p>FxmlMvcPatternDemo.java</p> <pre><code>package com.example.javafx.mvc; import java.util.ResourceBundle; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class FxmlMvcPatternDemo extends Application { public static void main(String[] args) throws ClassNotFoundException { Application.launch(FxmlMvcPatternDemo.class, args); } @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load ( FxmlMvcPatternDemo.class.getResource("MainView.fxml"), ResourceBundle.getBundle(FxmlMvcPatternDemo.class.getPackage().getName()+".MainView")/*properties file*/ ); stage.setScene(new Scene(root)); stage.show(); } } </code></pre> <p>MainView.fxml</p> <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;?import java.lang.*?&gt; &lt;?import javafx.scene.*?&gt; &lt;?import javafx.scene.control.*?&gt; &lt;?import javafx.scene.layout.*?&gt; &lt;VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.javafx.mvc.MainController" prefWidth="300" prefHeight="400" fillWidth="false" &gt; &lt;children&gt; &lt;Label text="%title" /&gt; &lt;ListView fx:id="choicesView" /&gt; &lt;Button text="Force Change" onAction="#handleForceChange" /&gt; &lt;/children&gt; &lt;/VBox&gt; </code></pre> <p>MainView.properties</p> <pre><code>title=JavaFX 2.0 FXML MVC demo </code></pre> <p>MainController.java</p> <pre><code>package com.example.javafx.mvc; import com.example.javafx.choice.ChoiceCell; import com.example.javafx.choice.ChoiceModel; import java.net.URL; import java.util.ResourceBundle; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.util.Callback; public class MainController implements Initializable { @FXML private ListView&lt;ChoiceModel&gt; choicesView; @Override public void initialize(URL url, ResourceBundle rb) { choicesView.setCellFactory(new Callback&lt;ListView&lt;ChoiceModel&gt;, ListCell&lt;ChoiceModel&gt;&gt;() { public ListCell&lt;ChoiceModel&gt; call(ListView&lt;ChoiceModel&gt; p) { return new ChoiceCell(); } }); choicesView.setItems(FXCollections.observableArrayList ( new ChoiceModel("Tiger", true), new ChoiceModel("Shark", false), new ChoiceModel("Bear", false), new ChoiceModel("Wolf", true) )); } @FXML private void handleForceChange(ActionEvent event) { if(choicesView != null &amp;&amp; choicesView.getItems().size() &gt; 0) { boolean isSelected = choicesView.getItems().get(0).isSelected(); choicesView.getItems().get(0).setSelected(!isSelected); } } } </code></pre> <p>ChoiceView.fxml</p> <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;?import java.lang.*?&gt; &lt;?import javafx.scene.*?&gt; &lt;?import javafx.scene.control.*?&gt; &lt;?import javafx.scene.layout.*?&gt; &lt;HBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.javafx.choice.ChoiceController" &gt; &lt;children&gt; &lt;CheckBox fx:id="isSelectedView" /&gt; &lt;Label fx:id="labelView" /&gt; &lt;/children&gt; &lt;/HBox&gt; </code></pre> <p>ChoiceController.java</p> <pre><code>package com.example.javafx.choice; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; public class ChoiceController { private final ChangeListener&lt;String&gt; LABEL_CHANGE_LISTENER = new ChangeListener&lt;String&gt;() { public void changed(ObservableValue&lt;? extends String&gt; property, String oldValue, String newValue) { updateLabelView(newValue); } }; private final ChangeListener&lt;Boolean&gt; IS_SELECTED_CHANGE_LISTENER = new ChangeListener&lt;Boolean&gt;() { public void changed(ObservableValue&lt;? extends Boolean&gt; property, Boolean oldValue, Boolean newValue) { updateIsSelectedView(newValue); } }; @FXML private Label labelView; @FXML private CheckBox isSelectedView; private ChoiceModel model; public ChoiceModel getModel() { return model; } public void setModel(ChoiceModel model) { if(this.model != null) removeModelListeners(); this.model = model; setupModelListeners(); updateView(); } private void removeModelListeners() { model.labelProperty().removeListener(LABEL_CHANGE_LISTENER); model.isSelectedProperty().removeListener(IS_SELECTED_CHANGE_LISTENER); isSelectedView.selectedProperty().unbindBidirectional(model.isSelectedProperty()) } private void setupModelListeners() { model.labelProperty().addListener(LABEL_CHANGE_LISTENER); model.isSelectedProperty().addListener(IS_SELECTED_CHANGE_LISTENER); isSelectedView.selectedProperty().bindBidirectional(model.isSelectedProperty()); } private void updateView() { updateLabelView(); updateIsSelectedView(); } private void updateLabelView(){ updateLabelView(model.getLabel()); } private void updateLabelView(String newValue) { labelView.setText(newValue); } private void updateIsSelectedView(){ updateIsSelectedView(model.isSelected()); } private void updateIsSelectedView(boolean newValue) { isSelectedView.setSelected(newValue); } } </code></pre> <p>ChoiceModel.java</p> <pre><code>package com.example.javafx.choice; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; public class ChoiceModel { private final StringProperty label; private final BooleanProperty isSelected; public ChoiceModel() { this(null, false); } public ChoiceModel(String label) { this(label, false); } public ChoiceModel(String label, boolean isSelected) { this.label = new SimpleStringProperty(label); this.isSelected = new SimpleBooleanProperty(isSelected); } public String getLabel(){ return label.get(); } public void setLabel(String label){ this.label.set(label); } public StringProperty labelProperty(){ return label; } public boolean isSelected(){ return isSelected.get(); } public void setSelected(boolean isSelected){ this.isSelected.set(isSelected); } public BooleanProperty isSelectedProperty(){ return isSelected; } } </code></pre> <p>ChoiceCell.java</p> <pre><code>package com.example.javafx.choice; import java.io.IOException; import java.net.URL; import javafx.fxml.FXMLLoader; import javafx.fxml.JavaFXBuilderFactory; import javafx.scene.Node; import javafx.scene.control.ListCell; public class ChoiceCell extends ListCell&lt;ChoiceModel&gt; { @Override protected void updateItem(ChoiceModel model, boolean bln) { super.updateItem(model, bln); if(model != null) { URL location = ChoiceController.class.getResource("ChoiceView.fxml"); FXMLLoader fxmlLoader = new FXMLLoader(); fxmlLoader.setLocation(location); fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory()); try { Node root = (Node)fxmlLoader.load(location.openStream()); ChoiceController controller = (ChoiceController)fxmlLoader.getController(); controller.setModel(model); setGraphic(root); } catch(IOException ioe) { throw new IllegalStateException(ioe); } } } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload