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

2022-09-04 22:05:30

如果自定义 JavaFX 组件对视图使用 FXML,如何正确扩展它来添加或修改其 GUI 组件?

作为示例场景,假设我使用以下方法来创建自定义 JavaFX 组件:

专用按钮.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 (View)

<?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>

我的应用.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();
    }
}

类加载 FXML 视图 (SpecializedButton.fxml) 并充当其控制器。

SpecializedButton FXML 视图只是创建一个 HBox,其中两个锚窗格以及一个标签和按钮分别位于左侧和右侧。单击该按钮时,它将调用 SpecializedButton 控制器类中的 doSomething()。


问题

通过此设置,我将使用 FXML 将视图与应用程序逻辑分开。

但是,如果我想创建一个新的高度专业化按钮类来扩展SpecializedButton,我不知道如何“扩展”视图。

如果我想使用SpecializedButton视图,但为HighpecializedButton修改它,我必须将视图代码复制到一个新的FXML文件中,从而导致重复的代码而不是正确的继承。

如果我没有使用FXML,而是在Java类中创建GUI组件,那么扩展该类将使我能够正确地从超类继承和添加/修改组件。

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


问题

在这种情况下,FXML 视图是否如此“愚蠢”,以至于每次我想从自定义组件继承时都必须创建一个全新的 FXML 视图?

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

我非常感谢任何见解。提前致谢!


答案 1

据我所知,您希望能够扩展现有组件,并避免复制和粘贴新组件的整个标记。

您可以使用注释为元件定义延伸点。看看 e.x. : 有一个用 的定义。这样,当您使用 e.x an(扩展 Pane)作为组件的根元素时,所有子组件都将添加到 Pane 定义的属性中。@DefaultPropertyjavafx.scene.layout.PaneObservableList getChildren()@DefaultProperty("children")HBoxchildren

以同样的方式,您可以在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


答案 2

我以前没有这样做过,但是您可以在实例化新的自定义组件时实现可初始化接口来设置视图组件。

添加新组件(标签、按钮、图标等)我会将所有子组件包装在视图文件上,放在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");
}  
}

因此,对于高度专业化的Button类:

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

}  
}

希望这可以帮助你


推荐