【问题标题】:How to create a custom FX component that renders it's children dinamically?如何创建一个动态呈现其子级的自定义 FX 组件?
【发布时间】:2020-06-24 10:48:35
【问题描述】:

我有一个代表表单字段的自定义组件。它有一个标签、一个文本字段和一个可能在输入验证后显示的错误消息。

<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="109.0" prefWidth="512.0" spacing="10.0" styleClass="vbox" type="VBox" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Label fx:id="fieldLabel" text="Lorem ipsum dolor sit amet"></Label>
      <TextField fx:id="textField" promptText="Lorem ipsum dolor sit amet"></TextField>
      <Label fx:id="errorLabel" text=""></Label>
   </children>
</fx:root>
public class FormField extends VBox {

    @FXML private TextField textField;
    @FXML private Label fieldLabel;
    @FXML private Label errorLabel;

    private String fieldLabelText;
    private String promptText;

    public FormField(@NamedArg("fieldLabelText") String fieldLabelText,
                     @NamedArg("promptText") String promptText,
                     @NamedArg("isPasswordField") boolean isPasswordField) {

        FXMLLoader loader = new FXMLLoader(getClass().getResource("../../resources/fxml/form-field.fxml"));
        loader.setRoot(this);
        loader.setController(this);

        this.fieldLabelText = fieldLabelText;
        this.promptText = promptText;

        try {
            loader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @FXML
    public void initialize() {
        this.fieldLabel.setText(fieldLabelText);
        this.textField.setPromptText(promptText);
    }

现在我想知道的是,我将如何对这个组件进行扩展,使其具有PasswordField 而不是TextField?或者传递一个参数,例如boolean isPasswordField,让FormField 决定它应该呈现TextField 还是PasswordField?如果 TextField 在它的 API 中有一个 obscureText(true) 方法就足够了,因为这就是我要找的所有东西,但我找不到任何东西。

我能找到的关于 JavaFX 继承的所有信息都是通过向对象添加新组件来“扩展”对象,而不是通过更改它的现有元素。

【问题讨论】:

标签: java javafx fxml


【解决方案1】:

一种选择是在 UI 中同时放置 TextFieldPasswordField,在 StackPane 中相互堆叠,其中只有一个可见:

<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="109.0" prefWidth="512.0" spacing="10.0" styleClass="vbox" type="VBox" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Label fx:id="fieldLabel" text="Lorem ipsum dolor sit amet"></Label>
      <StackPane>
        <TextField fx:id="textField" promptText="Lorem ipsum dolor sit amet"></TextField>
        <PasswordField fx:id="passwordField" visible="false"/>
      </StackPane>
      <Label fx:id="errorLabel" text=""></Label>
   </children>
</fx:root>

然后在控制器中根据传递的参数决定哪个是可见的:

public class FormField extends VBox {

    @FXML private TextField textField;
    @FXML private PasswordField passwordField;
    @FXML private Label fieldLabel;
    @FXML private Label errorLabel;

    private String fieldLabelText;
    private String promptText;
    private boolean isPasswordField;

    public FormField(@NamedArg("fieldLabelText") String fieldLabelText,
                     @NamedArg("promptText") String promptText,
                     @NamedArg("isPasswordField") boolean isPasswordField) {

        // path is copied from OP, but is incorrect:
        FXMLLoader loader = new FXMLLoader(getClass().getResource("../../resources/fxml/form-field.fxml"));
        loader.setRoot(this);
        loader.setController(this);

        this.fieldLabelText = fieldLabelText;
        this.promptText = promptText;
        this.isPasswordField = passwordField;

        try {
            loader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @FXML
    public void initialize() {
        this.fieldLabel.setText(fieldLabelText);
        this.textField.setPromptText(promptText);
        this.passwordField.promptTextProperty().bind(
            this.textField.promptTextProperty());
        this.passwordField.setVisible(isPasswordField);
        this.textField.setVisible(!isPasswordField);
    }

}

其他变体也是可能的,例如您的 FXML 可以只定义没有内容的 StackPane;然后在控制器代码中根据需要添加TextFieldPasswordField

【讨论】:

  • 在资源查找中传播不正确的参数.. doohh *tongue-in-cheek :)
  • 除此之外,如果他以后需要隐藏或显示文本,他可以使用布尔属性代替布尔值,用于文本是否隐藏,并绑定可见属性字段,并可能将字段的文本属性相互绑定(双向)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-07
  • 2020-01-09
相关资源
最近更新 更多