如何创建动态呈现其子项的自定义 FX 组件?
How to create a custom FX component that renders it's children dinamically?
我有一个代表表单域的自定义组件。它有一个标签、一个文本字段和一条可能在输入验证后显示的错误消息。
<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 继承的全部内容是通过向对象添加新组件而不是更改其现有元素来“扩展”对象。
一个选项是将 TextField
和 PasswordField
都放在 UI 中,在 StackPane
中彼此堆叠,只有一个 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
;然后在控制器代码中根据需要添加 TextField
或 PasswordField
。
我有一个代表表单域的自定义组件。它有一个标签、一个文本字段和一条可能在输入验证后显示的错误消息。
<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 继承的全部内容是通过向对象添加新组件而不是更改其现有元素来“扩展”对象。
一个选项是将 TextField
和 PasswordField
都放在 UI 中,在 StackPane
中彼此堆叠,只有一个 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
;然后在控制器代码中根据需要添加 TextField
或 PasswordField
。