Update:如需最新教程,请咨询官方文档 http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm。有很多新的东西 http://docs.oracle.com/javafx/2/fxml_get_started/whats_new2.htm这是在 2.2 中添加的。另外,FXML 简介 http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html几乎涵盖了您需要了解的有关 FXML 的所有内容。最后,Hendrik Ebbers 做了一个非常有帮助的博客文章 http://www.guigarage.com/2012/11/custom-ui-controls-with-javafx-part-1/关于自定义 UI 控件。
经过几天的环顾API http://docs.oracle.com/javafx/2.0/api/并阅读一些文档(FXML 简介 http://docs.oracle.com/javafx/2.0/api/javafx/fxml/doc-files/introduction_to_fxml.html, FXML 入门 http://docs.oracle.com/javafx/2.0/fxml_get_started/jfxpub-fxml_get_started.htm 属性绑定 http://docs.oracle.com/javafx/2.0/binding/jfxpub-binding.htm, FXML 的未来 http://fxexperience.com/2011/10/fxml-why-it-rocks-and-the-next-phase/)我想出了一个相当明智的解决方案。
我从这个小实验中学到的最不直接的信息是控制器的实例(在 FXML 中用 fx:controller 声明)由FXML加载器 http://docs.oracle.com/javafx/2.0/api/javafx/fxml/FXMLLoader.html加载 FXML 文件...最糟糕的是,这个重要事实仅在一个地方 http://docs.oracle.com/javafx/2.0/api/javafx/fxml/doc-files/introduction_to_fxml.html#controllers在我看到的所有文档中:
控制器通常仅对创建它的 FXML 加载程序可见
因此,请记住,为了以编程方式(从 Java 代码)获取对在 FXML 中声明的控制器实例的引用fx:controller
use FXMLLoader.getController() http://docs.oracle.com/javafx/2.0/api/javafx/fxml/FXMLLoader.html#getController%28%29(有关完整示例,请参阅下面 ChoiceCell 类的实现)。
另一件需要注意的是Property.bindBi Direction() http://docs.oracle.com/javafx/2.0/api/javafx/beans/property/Property.html#bindBidirectional%28javafx.beans.property.Property%29会将调用属性的值设置为作为参数传入的属性的值。给定两个布尔属性target
(最初设置为false
) and source
(最初设置为true
) 呼叫target.bindBidirectional(source)
将设置的值target
to true
。显然,对任一属性的任何后续更改都会更改另一个属性的值(target.set(false)
将导致值source
被设置为false
):
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
不管怎样,这里是演示 FXML 和 Java 如何协同工作的完整代码(以及其他一些有用的东西)
封装结构:
com.example.javafx.choice
ChoiceCell.java
ChoiceController.java
ChoiceModel.java
ChoiceView.fxml
com.example.javafx.mvc
FxmlMvcPatternDemo.java
MainController.java
MainView.fxml
MainView.properties
FxmlMvcPatternDemo.java
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();
}
}
主视图.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.javafx.mvc.MainController"
prefWidth="300"
prefHeight="400"
fillWidth="false"
>
<children>
<Label text="%title" />
<ListView fx:id="choicesView" />
<Button text="Force Change" onAction="#handleForceChange" />
</children>
</VBox>
MainView.properties
title=JavaFX 2.0 FXML MVC demo
主控制器.java
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<ChoiceModel> choicesView;
@Override
public void initialize(URL url, ResourceBundle rb)
{
choicesView.setCellFactory(new Callback<ListView<ChoiceModel>, ListCell<ChoiceModel>>()
{
public ListCell<ChoiceModel> call(ListView<ChoiceModel> 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 && choicesView.getItems().size() > 0)
{
boolean isSelected = choicesView.getItems().get(0).isSelected();
choicesView.getItems().get(0).setSelected(!isSelected);
}
}
}
ChoiceView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<HBox
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.javafx.choice.ChoiceController"
>
<children>
<CheckBox fx:id="isSelectedView" />
<Label fx:id="labelView" />
</children>
</HBox>
ChoiceController.java
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<String> LABEL_CHANGE_LISTENER = new ChangeListener<String>()
{
public void changed(ObservableValue<? extends String> property, String oldValue, String newValue)
{
updateLabelView(newValue);
}
};
private final ChangeListener<Boolean> IS_SELECTED_CHANGE_LISTENER = new ChangeListener<Boolean>()
{
public void changed(ObservableValue<? extends Boolean> 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);
}
}
ChoiceModel.java
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; }
}
ChoiceCell.java
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<ChoiceModel>
{
@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);
}
}
}
}