私はこのテーマに関する資料を見つけることができないようです。より具体的な例を挙げるために、チェックボックスとラベルを組み合わせたシンプルなコンポーネントを作成したいとします。次に、このカスタムコンポーネントのインスタンスをListViewに入力します。
更新:完全なコードについては私の答えをご覧ください
更新2:最新のチュートリアルについては、 公式ドキュメント を参照してください。 2.2で追加された new stuff がたくさんありました。最後に、 FXMLの紹介 は、FXMLについて知っておく必要のあるほとんどすべてを網羅しています。
更新3:Hendrik Ebbersは、カスタムUIコントロールについて非常に役立つ ブログ投稿 を作成しました。
更新:最新のチュートリアルについては、 公式ドキュメント を参照してください。 2.2で追加された new stuff がたくさんありました。また、 FXMLの概要 は、FXMLについて知っておく必要のあるほとんどすべてを網羅しています。最後に、Hendrik Ebbersは、カスタムUIコントロールについて非常に役立つ ブログ投稿 を作成しました。
[〜#〜] api [〜#〜] を調べていくつかのドキュメントを読んだ後( FXMLの概要 、 はじめにFXMLプロパティバインディング 、 FXMLの将来 )かなり賢明な解決策を思いつきました。この小さな実験から学んだ最も簡単な情報は、コントローラーのインスタンス(FXMLでfx:controllerで宣言されている)が、FXMLファイルをロードした FXMLLoader によって保持されていることです...最悪なことに、この重要な事実は、私が見たすべてのドキュメントの 1箇所 でのみ言及されています。
通常、コントローラーは、それを作成したFXMLローダーにのみ表示されます。
したがって、プログラムで(Javaコードから)コードを作成するために、_fx:controller
_を使用してFXMLで宣言されたコントローラーのインスタンスへの参照を取得 FXMLLoader.getController () (完全な例については、以下のChoiceCellクラスの実装を参照してください)。
注意すべきもう1つの点は、 Property.bindBiderctional() が呼び出し元のプロパティの値を、引数として渡されたプロパティの値に設定することです。 2つのブール型プロパティtarget
(元はfalse
に設定)およびsource
(最初にtrue
に設定)を指定すると、target.bindBidirectional(source)
を呼び出してtarget
〜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();
}
}
_
MainView.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
_
MainController.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);
}
}
}
}
_
JavaFx 2.1の場合、次の方法でカスタムFXMLコントロールコンポーネントを作成できます。
<?xml version="1.0" encoding="UTF-8"?>
<?import Java.lang.*?>
<?import Java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import customcontrolexample.myCommponent.*?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="customcontrolexample.FXML1Controller">
<children>
<MyComponent welcome="1234"/>
</children>
</VBox>
コンポーネントコード:
MyComponent.Java
package customcontrolexample.myCommponent;
import Java.io.IOException;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.util.Callback;
public class MyComponent extends Pane {
private Node view;
private MyComponentController controller;
public MyComponent() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("myComponent.fxml"));
fxmlLoader.setControllerFactory(new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> param) {
return controller = new MyComponentController();
}
});
try {
view = (Node) fxmlLoader.load();
} catch (IOException ex) {
}
getChildren().add(view);
}
public void setWelcome(String str) {
controller.textField.setText(str);
}
public String getWelcome() {
return controller.textField.getText();
}
public StringProperty welcomeProperty() {
return controller.textField.textProperty();
}
}
MyComponentController.Java
package customcontrolexample.myCommponent;
import Java.net.URL;
import Java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
public class MyComponentController implements Initializable {
int i = 0;
@FXML
TextField textField;
@FXML
protected void doSomething() {
textField.setText("The button was clicked #" + ++i);
}
@Override
public void initialize(URL location, ResourceBundle resources) {
textField.setText("Just click the button!");
}
}
myComponent.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import Java.lang.*?>
<?import Java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="customcontrolexample.myCommponent.MyComponentController">
<children>
<TextField fx:id="textField" prefWidth="200.0" />
<Button mnemonicParsing="false" onAction="#doSomething" text="B" />
</children>
</VBox>
このコードは、メモリリークがないかどうかを確認する必要があります。
簡単な答えは<fx:include>タグですが、ControllerクラスでChoiceModelを設定する必要があります。
<VBox
xmlns:fx="http://javafx.com/fxml"
fx:controller="fxmltestinclude.ChoiceDemo"
>
<children>
**<fx:include source="Choice.fxml" />**
<ListView fx:id="choices" />
</children>
</VBox>