如何扩展使用FXML的自定义JavaFX组件

15

如果一个自定义的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类加载FXML视图(SpecializedButton.fxml),并充当其控制器。

SpecializedButton FXML视图只创建一个HBox,左右两侧分别带有两个AnchorPanes和一个Label和Button。单击按钮时,它会调用SpecializedButton控制器类中的doSomething()方法。


问题

使用这种设置,我正在使用FXML将视图与应用程序逻辑分开。

但是,如果我想创建一个扩展SpecializedButton的新HighlySpecializedButton类,我不知道如何"扩展"视图。

如果我想要使用SpecializedButton视图,但为HighlySpecializedButton进行修改,则必须将视图代码复制到新的FXML文件中,这将导致重复的代码而不是正常的继承。

如果我不使用FXML,并在Java类中创建GUI组件,则扩展该类将使我能够正确继承并添加/修改来自父类的组件。

我显然对JavaFX的一些非常重要的概念有误解。


问题

在这种情况下,FXML视图是否过于"愚蠢",以至于每次想要从自定义组件继承时都必须创建全新的FXML视图?

或者,如果我误解了真正的问题,那么创建可扩展的自定义JavaFX组件的正确方法是什么,无论是否使用FXML?

我非常感谢任何见解。先感谢您!


1
这对我来说看起来只是过度使用继承:考虑使用聚合代替。但是,要按照您想要的方式实际执行它,您是否可以只使用 <fx:include>?或者,只需让“子类组件”的 FXML 引用 <SpecializedButton> - James_D
另一个问题:您是否曾经扩展过HTML文档?FXML是一种可编写脚本的基于XML的标记语言,用于构建Java对象图。这来自此处的文档:http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html - aw-think
2个回答

10
据我所了解,您希望能够扩展现有组件并避免复制和粘贴整个标记以创建新组件。
您可以使用@DefaultProperty注释为组件定义扩展点。例如,查看javafx.scene.layout.Pane:定义了一个ObservableList getChildren(),并用@DefaultProperty("children")进行了定义。 这样,当您将HBox(扩展Pane)用作组件的根元素时,所有子组件都会添加到Pane定义的children属性中。
同样,您也可以在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
}

SuperSpecializedButton.fxml

<fx:root type="SpecializedButton">
  <Label text="EXTENDED HALLO"/>
</fx:root>

并且:

public class SuperSpecializedButton extends SpecializedButton
{
    // ... more component specific code
}

你可以看到,我只需要为超级专业的组件定义fxml。

这段代码只适用于JavaFX >= 2.1。在此之前,使用FXMLLoader无法为超类进行组件注入。

我在github上添加了一个工作示例:https://github.com/matrak/javafx-extend-fxml


1

我以前没有做过这个,但是你可以实现 Initializable 接口,在实例化一个新的自定义组件时设置视图组件。

要添加新的组件(标签,按钮,图像等),我会在视图文件中将所有子组件包装在 AnchorPane、GridPane、VBox 或您喜欢的其他组件中,以便以编程方式添加更多组件。由于您正在从 HBox 扩展您的类,所以您已经可以这样做了。

这是我会做的:

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类:
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"));

}  
}

希望这能对你有所帮助。

他在询问FXML继承,而不是在Java代码中实现。 - aw-think
1
@NwDx 他还问道:“创建可扩展的自定义JavaFX组件的正确方法是什么,使用FXML或不使用?” - Abby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接