JavaFX 将 FXML 生成的节点更改为另一个节点

JavaFX change node generated by FXML to another node

我有一个由 fxml 文件定义的复杂 UI。有很多股票 TextField 我想更改为 ControlsFX 库提供的更高级版本(可清除文本字段)。

问题:

ControlsFX 使用 CustomTextField 提供高级功能,但它只能由 TextFields.createClearableTextField() 初始化,只能从代码中调用。

我正在寻找一种方法来交换通过 FXML 初始化的 TextFields 和从代码初始化的 CustomTextFields,这样这些新控件将保留 FXML 中定义的所有布局属性。

在 FXML 中定义 CustomTextField 也没有帮助,因为默认情况下它是无用的。 CustomTextField 通过 private static void setupClearButtonField(TextField inputField, ObjectProperty<Node> rightProperty) 获得它的功能,我不能调用它,因为它是私有的。

ControlsFX代码:

/**
 * Creates a TextField that shows a clear button inside the TextField (on
 * the right hand side of it) when text is entered by the user.
 */
public static TextField createClearableTextField() {
    CustomTextField inputField = new CustomTextField();
    setupClearButtonField(inputField, inputField.rightProperty());
    return inputField;
}

/**
 * Creates a PasswordField that shows a clear button inside the PasswordField
 * (on the right hand side of it) when text is entered by the user.
 */
public static PasswordField createClearablePasswordField() {
    CustomPasswordField inputField = new CustomPasswordField();
    setupClearButtonField(inputField, inputField.rightProperty());
    return inputField;
}

private static void setupClearButtonField(TextField inputField, ObjectProperty<Node> rightProperty) {
    inputField.getStyleClass().add("clearable-field"); //$NON-NLS-1$

    Region clearButton = new Region();
    clearButton.getStyleClass().addAll("graphic"); //$NON-NLS-1$
    StackPane clearButtonPane = new StackPane(clearButton);
    clearButtonPane.getStyleClass().addAll("clear-button"); //$NON-NLS-1$
    clearButtonPane.setOpacity(0.0);
    clearButtonPane.setCursor(Cursor.DEFAULT);
    clearButtonPane.setOnMouseReleased(e -> inputField.clear());
    clearButtonPane.managedProperty().bind(inputField.editableProperty());
    clearButtonPane.visibleProperty().bind(inputField.editableProperty());

    rightProperty.set(clearButtonPane);

    final FadeTransition fader = new FadeTransition(FADE_DURATION, clearButtonPane);
    fader.setCycleCount(1);

    inputField.textProperty().addListener(new InvalidationListener() {
        @Override public void invalidated(Observable arg0) {
            String text = inputField.getText();
            boolean isTextEmpty = text == null || text.isEmpty();
            boolean isButtonVisible = fader.getNode().getOpacity() > 0;

            if (isTextEmpty && isButtonVisible) {
                setButtonVisible(false);
            } else if (!isTextEmpty && !isButtonVisible) {
                setButtonVisible(true);
            }
        }

        private void setButtonVisible( boolean visible ) {
            fader.setFromValue(visible? 0.0: 1.0);
            fader.setToValue(visible? 1.0: 0.0);
            fader.play();
        }
    });
}

您可以按照 Introduction to FXML 中的说明使用 fx:factory

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.StackPane?>
<?import org.controlsfx.control.textfield.TextFields?>

<StackPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
           fx:controller="com.example.Controller">

    <TextFields fx:id="field" fx:factory="createClearableTextField"/>

</StackPane>

然后在控制器中使用:

package com.example;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class Controller {

    @FXML private TextField field;

}

注意:如果您使用的是 IntelliJ,它会发出一条错误消息:

Cannot set org.controlsfx.control.textfield.TextFields to field 'field'

在 FXML 文件中,但是当我 运行 测试项目时一切正常。