JavaF XML создать пользовательский элемент тега, который не расширяет узел - PullRequest
0 голосов
/ 13 марта 2020

Я хотел бы создать пользовательский tag / element для моего fxml файла, но я не хочу, чтобы этот элемент принудительно наследовался от Pane, Button, TextField и т. Д. на. Есть ли некоторый интерфейс, который может быть реализован в моем пользовательском элементе, который потребует от меня реализации, скажем, метод fx(), который требуется для возврата элемента Node / Region, который должен быть визуализирован в Scene. Что я имею в виду, если возможно что-то вроде следующего?

public class CustomElement implements SOME_FXML_INTERFACE {

    private String myArg;

    public CustomElement(@NamedArg("myArg") myArg) {
        this.myArg = myArg;
    }

    // method that is required to be implemented by SOME_FXML_INTERFACE
    // this method retuns some GUI element which actually needs to be rendered in the Scene
    @Override
    public Object fx() {
        return new TextField(myArg);
    }

}


<HBox>
    <CustomElement myArg="some_argument"/>
</HBox>

Все это так, чтобы у меня мог быть CustomElement, который может принимать пользовательский аргумент в конструкторе.

Ответы [ 2 ]

1 голос
/ 13 марта 2020

Вы можете создавать произвольные объекты, но фабричные объекты могут использоваться только узлами, которые поддерживают это. (Технически вы могли бы сделать это, но это потребовало бы использования получателя, возвращающего новый экземпляр каждый раз, когда он вызывается.) Однако вы можете, например, использовать свой пользовательский класс как cellFactory для ListView.

Если вы делаете не нужно полагаться на метод экземпляра, но он удовлетворен фабричным методом static, вы можете использовать тег fx:factory, чтобы указать метод для создания экземпляра узла, и вам не нужно реализовывать какой-либо интерфейс для этого:

package my.package;

...

public class CustomElement {

    public static Node fx() {
        return new TextField(myArg);
    }

}
<?import my.package.CustomElement?>
<?import javafx.scene.layout.HBox?>

<HBox xmlns:fx="http://javafx.com/fxml">
    <children>
        <CustomElement fx:factory="fx" />
    </children>
</HBox>

Более подробная информация доступна в Введение в F XML

Кстати: если в вашем классе есть конструктор public, не принимающий никаких Параметры вы можете создать экземпляр этого класса, просто добавив элемент с именем класса в f xml. Например, следующее f xml приводит к ArrayList при загрузке; способы использования этих экземпляров ограничены;

<?import java.util.ArrayList?>

<ArrayList />
0 голосов
/ 13 марта 2020

Помимо использования фабричного метода, например , описанного fabian , вы можете использовать механизм компоновщика для этого. Как и в случае <fx:factory>, который ограничен методами static без аргументов, использование немного ограничено. Однако вот пример фабрики / компоновщика, который создает TextField или Label, в зависимости от того, установлен ли флаг editable:

package org.jamesd.examples.fxmlfactory;

import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.util.Builder;

public class Factory implements Builder<Node> {

    private String text = "" ;
    private boolean editable = false ;



    public String getText() {
        return text;
    }



    public void setText(String text) {
        this.text = text;
    }



    public boolean isEditable() {
        return editable;
    }



    public void setEditable(boolean editable) {
        this.editable = editable;
    }



    @Override
    public Node build() {
        if (editable) {
            return new TextField(text);
        } else {
            return new Label(text);
        }
    }


}

Вот файл F XML, который использует this:

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

<?import javafx.scene.layout.BorderPane ?>
<?import javafx.scene.layout.HBox ?>
<?import javafx.scene.Node ?>


<BorderPane xmlns="http://javafx.com/javafx/11.0.1"
    xmlns:fx="http://javafx.com/fxml/1">

    <center>
        <HBox spacing="5">
            <Node text="Hello" editable="false" />
            <Node text="Name" editable="true" />
        </HBox>
    </center>
</BorderPane>

Чтобы FXMLLoader знал о вашем сборщике, вы должны указать класс, для которого он будет использоваться. Здесь мы можем использовать его для Node (но это требование немного ограничительно, так как вы будете существенно ограничены одним компоновщиком). Вы делаете это, создавая BuilderFactory реализацию:

package org.jamesd.examples.fxmlfactory;

import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Node;
import javafx.util.Builder;
import javafx.util.BuilderFactory;

public class NodeBuilderFactory implements BuilderFactory{

    private final JavaFXBuilderFactory defaultFactory = new JavaFXBuilderFactory();

    public Builder<?> getBuilder(Class<?> type) {
        if (type == Node.class) {
            return new Factory();
        }
        return defaultFactory.getBuilder(type);
    }

}

и затем регистрируя ее с помощью FXMLLoader:

package org.jamesd.examples.fxmlfactory;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        FXMLLoader loader = new FXMLLoader(getClass().getResource("Example.fxml"));
        loader.setBuilderFactory(new NodeBuilderFactory());
        Parent root = loader.load();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

}
...