如何专门化 JavaFX 的组件并隐藏其 API 以仅显示我的

How to specialize a components of JavaFX and hide its API to only show mine

如标题中所述,我想专门介绍我将多次重复使用的 TableView 的用法,该规范包含:

我使用原始 FXML 文件和控制器,没有 UI 拖放构建。

为了尽可能简单地使用我的组件,我想隐藏 JavaFX 的组件部分,只允许我的方法,该怎么做?

这是我发现的当前使用接口的方法,并在使用 @FXML 注入时使用该接口。

我的专业组件:

public class EquipmentTableView extends AnchorPane implements IEquipmentTableView{[...]}

其中AnchorPane来自JavaFX,界面来自我。

组件的 FXML :

<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
    <TableView fx:id="tableView"
        AnchorPane.topAnchor="0.0" AnchorPane.bottomAnchor="0.0"
        AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">

    </TableView>
</fx:root>

要包含组件的 FXML :

<EquipmentTableView fx:id="tableView"/>

控制器中的注入

public class ReadController {  
        // use the interface here 
        @FXML private IEquipmentTableView tableView;

        public static String FXML="read.fxml";
        public static String TITLE_KEY="read.title";

        @FXML
        void initialize(){
            tableView.addAll(Arrays.asList(
                    new Equipment("name1", "partNumber1", "MFC1", "00000000001", "3400000000000001"),
                    new Equipment("name2", "partNumber2", "MFC2", "00000000002", "3400000000000002"),
                    new Equipment("name3", "partNumber3", "MFC3", "00000000003", "3400000000000003")));
        }
}

总结一下:

  • 通过在组件内部使用 TableView 而不是继承它,我不允许操纵 TableView。我改为继承 AnchorPane,因为我希望我的 table 视图自动调整大小,否则您可以从 Pane 继承。
  • 因为我无法访问 TableView,所以我只能使用接口方法来操作它。
  • 仅使用界面不允许我出于其他目的操纵 AnchorPane 或必须搜索我的方法。

创建一个 class 扩展 Control。将您希望用户访问的方法添加到此 class.

为此创建一个皮肤 class 并实现您不希望用户在那里访问的行为。

好处:

  • 对用户隐藏实施细节。
  • 如果需要,允许用户替换 "private" 行为。
  • 允许您直接从节点访问 "public" 行为。

例子

控制

public class MyControl extends Control {

    @Override
    protected Skin<?> createDefaultSkin() {
        return new MyControlSkin(this);
    }

    private final StringProperty text = new SimpleStringProperty();

    public final String getText() {
        return this.text.get();
    }

    public final void setText(String value) {
        this.text.set(value);
    }

    public final StringProperty textProperty() {
        return this.text;
    }

}

皮肤

public class MyControlSkin extends SkinBase<MyControl> {

    public MyControlSkin(MyControl control) {
        super(control);

        Text text = new Text();
        text.textProperty().bind(control.textProperty());

        getChildren().setAll(text);
    }

}

使用

@Override
public void start(Stage primaryStage) {
    final MyControl control = new MyControl();
    Button btn = new Button("Say 'Hello World'");
    btn.setOnAction((ActionEvent event) -> {
        control.setText("Hello World!");
    });

    Scene scene = new Scene(new VBox(10, btn, control));

    primaryStage.setScene(scene);
    primaryStage.show();
}

请注意,您如何创建 UI 并不重要。它可以直接从 java 创建或从 fxml 加载 - 这无关紧要。你可以例如使用皮肤作为 fxml 控制器和根:

<fx:root type="javafx.scene.control.SkinBase" xmlns:fx="http://javafx.com/fxml">
    <children>
        <Text fx:id="text"/>
    </children>
</fx:root>
@FXML
private Text text;

public MyControlSkin(MyControl control) throws IOException {
    super(control);
    getChildren().clear();

    FXMLLoader loader = new FXMLLoader(someUrl);
    loader.setRoot(this);
    loader.setController(this);
    loader.load();

    text.textProperty().bind(control.textProperty());
}

顺便说一句:恕我直言,重复过滤在单独的 class 中会更好,例如 TransformationList。通过这种方式,您可以重用独立于 UI 的行为,这样可以更轻松地重用(例如,与 ListView 一起使用)。