java - 如何创建自定义 FX 组件以动态呈现其子级?
问题描述
我有一个代表表单字段的自定义组件。它有一个标签、一个文本字段和一个可能在输入验证后显示的错误消息。
<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);
}
现在我想知道的是,我将如何扩展这个具有 aPasswordField
而不是 a 的组件TextField
?或者传递一个参数boolean isPasswordField
,让我们FormField
决定它应该呈现 aTextField
还是 a PasswordField
?如果在它的 API 中TextField
有一个obscureText(true)
方法就足够了,因为这就是我要找的所有东西,但我找不到任何东西。
我能找到的关于 JavaFX 继承的所有信息都是通过向对象添加新组件来“扩展”对象,而不是通过更改它的现有元素。
解决方案
一种选择是将 aTextField
和 aPasswordField
放在 UI 中,在 a 中彼此堆叠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
没有内容的;然后在控制器代码中添加 aTextField
或PasswordField
根据需要。
推荐阅读
- javascript - 在 For 循环中使用 Mongoose FindoneAndUpdate
- google-cloud-platform - 在 DataFusion 中读取 Excel 并在读取时进行编辑
- reactjs - 如何将创建的 React Leaflet 层发布到 API?
- c++ - 在 64 位上调整 std::vector 大小的 bad_alloc 错误。它似乎将其限制为 32 位
- php - curl请求在一定数量的帖子后返回0
- c# - 如何仅在 itext 7 表中每页(最终)的第一行重复列值?
- typescript - TS2345:“字符串”类型的参数不可分配给“从不”类型的参数。从 idb 升级到 typescript 4.4
- javascript - ReactJS Async - 使用 React Context 模拟单例?
- android - 谷歌静态地图 API url 在 android 中不起作用
- javascript - React Native Wheel Color Picker - naeemur - 不确定如何实际访问颜色