使用FXMLLoader加载FXML文件,调用load()方法,得到FXML根节点对象,然后就可以像使用JavaFX本身的控件一样使用了。
FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginPaneFx.fxml"));
// Set root if FXML root node is <fx:root/>
loader.setRoot(this);
// Set controller if FXML doesn't include a controller.
loader.setController(this);
GridPane loginPane = loader.<GridPane>load();
1. 定义FXML文件
1.1 定义根节点类型
JavaFX 2之前,根节点类型是以XML tag方式定义的,使用FXMLLoader时,不用也不能调用setRoot()方法。
<GridPane xmlns:fx="http://javafx.com/fxml/1"> ...... </GridPane>
JavaFX 2版本之后,FXML提供<fx:root/>写法,此时要求Controller必须继承自FXML节点对象,使用FXMLLoader加载时,必须调用setRoot()方法。
<fx:root type="GridPane" xmlns:fx="http://javafx.com/fxml/1"> ...... </fx:root>
SceneBuilder左下角Controller面板,可以勾选是否使用<fx:root/>方式定义FXML。
1.2 FXML中指定Controller
在Controller面板里面,可以给FXML指定Controller(包名+类名)。如果FXML没有指定,就需要在code中调用FXMLLoader setController()方法,指定一个Controller。
SceneBuilder中可以查看FXML对应Controller大致框架,copy到code中就可以用了。Menu->View->”Show Sample Controller Skeelton”。
1.3 FXML最佳实践
根节点使用<fx:root/>方式定义,可以将Controller类型和FXML根节点类型保持一致,方便使用。
不要在FXML中指定Controller,通常情况下一个FXML可以对应多个Controller,为了灵活性,我们应当在FXMLLoader中指定Controller。
2. FXML和Controller耦合在一起
2.1 FXML sample
根节点类型是GridPane,使用<fx:root/>定义,FXML里没有指定Controller。
<fx:root type="GridPane" xmlns:fx="http://javafx.com/fxml/1"> ...... </fx:root>
Controller继承GridPane,因为和FXML根节点同类型,其实就是FXML控件本身。
2.2 耦合的Controller
public class LoginPaneFx extends GridPane {
@FXML
private TextField nameTxt;
public LoginPaneFx() {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("LoginPaneFx.fxml"));
loader.setRoot(this);
loader.setController(this);
loader.load();
} catch (Exception e) {
e.printStackTrace();
}
}
}
此时Controller LoginPaneFx就和JavaFX其它控件一样使用。
3. FXML和Controller完全独立
3.1 FXML sample
根节点是GridPane,不使用<fx:root/>,FXML里没有指定Controller。
<GridPane xmlns:fx="http://javafx.com/fxml/1"> ...... </GridPane>
3.2 独立的Controller
实现Initializable接口,initialize()方法会在JavaFX Injection(field injection和method injection)之后,自动调用,方便对注入的JavaFX控件进行初始化。
public class LoginPaneController implements Initializable {
@FXML
private TextField nameTxt;
@FXML
public void loginAction() {
System.out.println("Do login");
}
@Override
public void initialize(URL location, ResourceBundle resources) {
// This method will be call after all FXML injection completed.
// We can initialize JavaFX control here.
nameTxt.textProperty().addListener((obversable, oldVal, newVal) -> {
System.out.println("Text changed.");
});
}
}
创建一个JavaFX程序,使用该FXML控件。
public class LoginFX extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
LoginFX.class.getResource("LoginPaneFx.fxml"));
LoginPaneController controller = new LoginPaneController();
loader.setController(controller);
GridPane loginPane = loader.<GridPane> load();
stage.setScene(new Scene(loginPane));
stage.setTitle("Login");
stage.show();
}
}
4. 总结
耦合式的Controller,里面封装了FXMLLoader的相关行为,方便使用者,和使其它JavaFX控件一样使用。
独立式的Controller,代码简洁,完全使用@FXML注入。使用起来稍微不太方便,需要单独构建一个FXMLLoader.
我Google了很多教程,关于如何创建自定义控件,大部分都是采用耦合式的。具体哪个好,哪个坏,各有千秋。