如何扩展使用 FXML 的自定义 JavaFX 组件
How to extend custom JavaFX components that use FXML
如果自定义 JavaFX 组件将 FXML 用于视图,我该如何正确扩展它以添加或修改其 GUI 组件?
作为示例场景,假设我使用以下方法创建自定义 JavaFX 组件:
SpecializedButton.java(控制器)
package test;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class SpecializedButton extends HBox
{
@FXML private Button button;
@FXML private Label label;
public SpecializedButton()
{
FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );
loader.setRoot( this );
loader.setController( this );
try {
loader.load();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
// specialized methods for this specialized button
// ...
public void doSomething() {
button.setText( "Did something!" );
}
}
SpecializedButton.fxml(查看)
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<fx:root type="HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<AnchorPane HBox.hgrow="ALWAYS">
<children>
<Label fx:id="label" text="Click to do something: " AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane HBox.hgrow="ALWAYS">
<children>
<Button fx:id="button" onAction="#doSomething" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</children>
</fx:root>
MyApp.java
package test;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MyApp extends Application
{
public static void main( String[] args )
{
launch( args );
}
public void start( Stage stage ) throws Exception
{
stage.setScene( new Scene( new SpecializedButton(), 300, 50 ) );
stage.show();
}
}
SpecializedButton class 加载 FXML 视图 (SpecializedButton.fxml) 并充当它的控制器。
SpecializedButton FXML 视图只是创建了一个 HBox,它在左右两侧分别有两个 AnchorPanes 和一个 Label 和 Button。单击按钮时,它会调用 SpecializedButton 控制器中的 doSomething() class。
问题
通过此设置,我使用 FXML 将视图与应用程序逻辑分开。
但是,如果我想创建一个新的 HighlySpecializedButton class 来扩展 SpecializedButton,我不知道如何处理 "extending" 视图以及。
如果我想使用 SpecializedButton 视图,但为 HighlySpecializedButton 修改它,我必须将视图代码复制到新的 FXML 文件中,从而导致重复代码而不是正确的继承。
如果我没有使用 FXML,而是在 Java class 中创建 GUI 组件,那么扩展 class 将使我能够正确继承和 add/modify 来自超级 class.
的组件
我显然误解了关于 JavaFX 的一些非常重要的概念。
问题
在这种情况下,FXML 视图是否 "dumb" 每次我想从自定义组件继承时都必须创建一个全新的 FXML 视图?
或者,如果我误解了真正的问题,创建可扩展自定义 JavaFX 组件的正确方法是什么,有或没有 FXML?
如果有任何见解,我将不胜感激。提前致谢!
我以前没有这样做过,但是您可以在实例化新的自定义组件时实现 Initializable 接口来设置视图组件。
要添加新组件(标签、按钮、img 等...),我会将视图文件中的所有子组件包装在 AnchorPane、GridPane、VBox 或您喜欢以编程方式添加更多组件的组件中.由于您正在从 HBox 扩展 class,因此您已经可以这样做了。
我会这样做:
public class SpecializedButton extends HBox implements Initializable{
@FXML private Button button;
@FXML private Label label;
public SpecializedButton(){
FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );
loader.setRoot( this );
loader.setController( this );
try {
loader.load();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
// specialized methods for this specialized button
// ...
public void doSomething() {
button.setText( "Did something!" );
}
@Override
public void initialize(URL url, ResourceBundle rb) {
button.setText("Initialized Text");
}
}
所以对于 HighlySpecializedButton class:
public class HighlySpecializedButton extends SpecializedButton {
public HighlySpecializedButton (){
super();
}
// HighlySpecializedButton methods
@Override
public void initialize(URL url, ResourceBundle rb) {
this.getChildren().add(new Button("New Button"));
}
}
希望对您有所帮助
据我了解,您希望能够扩展现有组件并避免
复制并粘贴新组件的整个标记。
您可以使用 @DefaultProperty
注释为组件定义扩展点。
看看e.x。 javafx.scene.layout.Pane
:有一个ObservableList getChildren()
用 @DefaultProperty("children")
定义。
这样,当您使用 e.x 和 HBox
(扩展窗格)作为组件的根元素时,所有子组件都会添加到由窗格。
您可以用相同的方式在 FXML 中定义扩展点:
SpecializedButton.fxml(我为这个例子简化了一点)
<fx:root type="HBox">
<HBox>
<Label fx:id="label" text="Click to do something: " />
<HBox fx:id="extension"></HBox>
</HBox>
<HBox>
<Button fx:id="button" onAction="#doSomething"/>
</HBox>
</fx:root>
HBox fx:id="extension"
将是我的扩展点:
@DefaultProperty(value = "extension")
public class SpecializedButton extends HBox
{
@FXML private HBox extension;
public ObservableList<Node> getExtension() {
return extension.getChildren();
}
// ... more component specific code
}
超级SpecializedButton.fxml
<fx:root type="SpecializedButton">
<Label text="EXTENDED HALLO"/>
</fx:root>
并且:
public class SuperSpecializedButton extends SpecializedButton
{
// ... more component specific code
}
如您所见,我只需要为超级专用组件定义fxml。
This code will work only on JavaFX >= 2.1。之前,超级 class 的组件注入不适用于 FXMLLoader。
我在 github 上添加了一个工作示例:https://github.com/matrak/javafx-extend-fxml
如果自定义 JavaFX 组件将 FXML 用于视图,我该如何正确扩展它以添加或修改其 GUI 组件?
作为示例场景,假设我使用以下方法创建自定义 JavaFX 组件:
SpecializedButton.java(控制器)
package test;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class SpecializedButton extends HBox
{
@FXML private Button button;
@FXML private Label label;
public SpecializedButton()
{
FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );
loader.setRoot( this );
loader.setController( this );
try {
loader.load();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
// specialized methods for this specialized button
// ...
public void doSomething() {
button.setText( "Did something!" );
}
}
SpecializedButton.fxml(查看)
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<fx:root type="HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<AnchorPane HBox.hgrow="ALWAYS">
<children>
<Label fx:id="label" text="Click to do something: " AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane HBox.hgrow="ALWAYS">
<children>
<Button fx:id="button" onAction="#doSomething" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</children>
</fx:root>
MyApp.java
package test;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MyApp extends Application
{
public static void main( String[] args )
{
launch( args );
}
public void start( Stage stage ) throws Exception
{
stage.setScene( new Scene( new SpecializedButton(), 300, 50 ) );
stage.show();
}
}
SpecializedButton class 加载 FXML 视图 (SpecializedButton.fxml) 并充当它的控制器。
SpecializedButton FXML 视图只是创建了一个 HBox,它在左右两侧分别有两个 AnchorPanes 和一个 Label 和 Button。单击按钮时,它会调用 SpecializedButton 控制器中的 doSomething() class。
问题
通过此设置,我使用 FXML 将视图与应用程序逻辑分开。
但是,如果我想创建一个新的 HighlySpecializedButton class 来扩展 SpecializedButton,我不知道如何处理 "extending" 视图以及。
如果我想使用 SpecializedButton 视图,但为 HighlySpecializedButton 修改它,我必须将视图代码复制到新的 FXML 文件中,从而导致重复代码而不是正确的继承。
如果我没有使用 FXML,而是在 Java class 中创建 GUI 组件,那么扩展 class 将使我能够正确继承和 add/modify 来自超级 class.
的组件我显然误解了关于 JavaFX 的一些非常重要的概念。
问题
在这种情况下,FXML 视图是否 "dumb" 每次我想从自定义组件继承时都必须创建一个全新的 FXML 视图?
或者,如果我误解了真正的问题,创建可扩展自定义 JavaFX 组件的正确方法是什么,有或没有 FXML?
如果有任何见解,我将不胜感激。提前致谢!
我以前没有这样做过,但是您可以在实例化新的自定义组件时实现 Initializable 接口来设置视图组件。
要添加新组件(标签、按钮、img 等...),我会将视图文件中的所有子组件包装在 AnchorPane、GridPane、VBox 或您喜欢以编程方式添加更多组件的组件中.由于您正在从 HBox 扩展 class,因此您已经可以这样做了。
我会这样做:
public class SpecializedButton extends HBox implements Initializable{
@FXML private Button button;
@FXML private Label label;
public SpecializedButton(){
FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );
loader.setRoot( this );
loader.setController( this );
try {
loader.load();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
// specialized methods for this specialized button
// ...
public void doSomething() {
button.setText( "Did something!" );
}
@Override
public void initialize(URL url, ResourceBundle rb) {
button.setText("Initialized Text");
}
}
所以对于 HighlySpecializedButton class:
public class HighlySpecializedButton extends SpecializedButton {
public HighlySpecializedButton (){
super();
}
// HighlySpecializedButton methods
@Override
public void initialize(URL url, ResourceBundle rb) {
this.getChildren().add(new Button("New Button"));
}
}
希望对您有所帮助
据我了解,您希望能够扩展现有组件并避免 复制并粘贴新组件的整个标记。
您可以使用 @DefaultProperty
注释为组件定义扩展点。
看看e.x。 javafx.scene.layout.Pane
:有一个ObservableList getChildren()
用 @DefaultProperty("children")
定义。
这样,当您使用 e.x 和 HBox
(扩展窗格)作为组件的根元素时,所有子组件都会添加到由窗格。
您可以用相同的方式在 FXML 中定义扩展点:
SpecializedButton.fxml(我为这个例子简化了一点)
<fx:root type="HBox">
<HBox>
<Label fx:id="label" text="Click to do something: " />
<HBox fx:id="extension"></HBox>
</HBox>
<HBox>
<Button fx:id="button" onAction="#doSomething"/>
</HBox>
</fx:root>
HBox fx:id="extension"
将是我的扩展点:
@DefaultProperty(value = "extension")
public class SpecializedButton extends HBox
{
@FXML private HBox extension;
public ObservableList<Node> getExtension() {
return extension.getChildren();
}
// ... more component specific code
}
超级SpecializedButton.fxml
<fx:root type="SpecializedButton">
<Label text="EXTENDED HALLO"/>
</fx:root>
并且:
public class SuperSpecializedButton extends SpecializedButton
{
// ... more component specific code
}
如您所见,我只需要为超级专用组件定义fxml。
This code will work only on JavaFX >= 2.1。之前,超级 class 的组件注入不适用于 FXMLLoader。
我在 github 上添加了一个工作示例:https://github.com/matrak/javafx-extend-fxml