javafxcustomcontrol<;T>;:这可能吗?

javafxcustomcontrol<;T>;:这可能吗?,java,generics,custom-controls,javafx-8,scenebuilder,Java,Generics,Custom Controls,Javafx 8,Scenebuilder,我想在JavaFX中创建一个简单的可重用自定义控件,它只不过是一个带有标签的组合框,可以设置文本 我希望它在JavaFX场景生成器中可用 我还希望它能够采用单个通用参数,尽可能地模拟可用的标准组合框的行为 我遇到的问题是,当我尝试在SceneBuilder中将Controls Controller设置为Controller时,我收到一个错误,告诉我:控制器对于控制器类无效 这就像调用fxmloader.load()(在设置根、类加载器和位置之后)时,无法(我可以找到)告诉加载器“哦,这是一个Cu

我想在JavaFX中创建一个简单的可重用自定义控件,它只不过是一个带有标签的组合框,可以设置文本

我希望它在JavaFX场景生成器中可用

我还希望它能够采用单个通用参数
,尽可能地模拟可用的标准
组合框的行为

我遇到的问题是,当我尝试在SceneBuilder中将Controls Controller设置为
Controller
时,我收到一个错误,告诉我:
控制器对于控制器类无效

这就像调用
fxmloader.load()
(在设置根、类加载器和位置之后)时,无法(我可以找到)告诉加载器“哦,这是一个CustomControl。”

这是我对控件的代码:

public class LabeledComboBox<T> extends VBox {
    private final LCBController<T> Controller;
    public LabeledComboBox(){
        this.Controller = this.Load();
    }

    private LCBController Load(){
        final FXMLLoader loader = new FXMLLoader();
        loader.setRoot(this);
        loader.setClassLoader(this.getClass().getClassLoader());
        loader.setLocation(this.getClass().getResource("LabeledComboBox.fxml"));
        try{
            final Object root = loader.load();
            assert root == this;
        } catch (IOException ex){
            throw new IllegalStateException(ex);
        }

        final LCBController ctrlr = loader.getController();
        assert ctrlr != null;
        return ctrlr;
    }
    /*Methods*/
}
public类LabeledComboBox扩展了VBox{
专用最终LCB控制器;
公共标签mbobox(){
this.Controller=this.Load();
}
专用LCB控制器加载(){
最终FXMLLoader=新的FXMLLoader();
setRoot(this);
setClassLoader(this.getClass().getClassLoader());
setLocation(this.getClass().getResource(“LabeledComboBox.fxml”);
试一试{
最终对象根=loader.load();
断言根==this;
}捕获(IOEX异常){
抛出新的非法状态异常(ex);
}
最终LCBController ctrlr=loader.getController();
断言ctrlr!=null;
返回ctrlr;
}
/*方法*/
}
这是控制器类:

public class LCBController<T> implements Initializable {
    //<editor-fold defaultstate="collapsed" desc="Variables">
    @FXML private ResourceBundle resources;

    @FXML private URL location;

    @FXML private Label lbl; // Value injected by FXMLLoader

    @FXML private ComboBox<T> cbx; // Value injected by FXMLLoader
    //</editor-fold>
    //<editor-fold defaultstate="collapsed" desc="Initialization">
    @Override public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
        this.location = fxmlFileLocation;
        this.resources = resources;
    //<editor-fold defaultstate="collapsed" desc="Assertions" defaultstate="collapsed">
        assert lbl != null : "fx:id=\"lbl\" was not injected: check your FXML file 'LabeledComboBox.fxml'.";
        assert cbx != null : "fx:id=\"cbx\" was not injected: check your FXML file 'LabeledComboBox.fxml'.";
    //</editor-fold>
    }
    //</editor-fold>
    /*Methods*/
}
公共类LCBController实现可初始化{
//
@FXML私有资源包资源;
@FXML私有URL位置;
@FXML私有标签lbl;//FXMLLoader注入的值
@FXML专用组合框cbx;//FXMLLoader注入的值
//
//
@重写公共void初始化(URL fxmlFileLocation,ResourceBundle资源){
this.location=fxmlFileLocation;
这就是资源=资源;
//
assert lbl!=null:“fx:id=\“lbl\”未被注入:检查FXML文件“LabeledComboBox.FXML”;
assert cbx!=null:“fx:id=\“cbx\”未被注入:检查FXML文件“LabeledComboBox.FXML”;
//
}
//
/*方法*/
}
很明显,我在这里遗漏了一些东西。我真的希望这是可能的,而不必拿出我自己的FXMLLoader类实现(真的,真的,真的希望)

有人能告诉我我错过了什么,或者这是否可能

编辑1: 在有人给我指了一个链接之后,我可能已经知道了怎么做,但我仍然不是百分之百的确定。对我来说,它感觉控制器类本身不能用泛型参数创建(即:
公共类控制器{…}
=不好) 这有点烦人,但我想是有道理的

那么,如何将泛型参数应用于自定义控件控制器内的方法,并使控件本身(而不是控制器)成为泛型:如是

控制:

public class LabeledComboBox<T> extends VBox {...}
package application;

import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.layout.VBox;

public class LabeledComboBox<T> extends VBox {

    private final LabeledComboBoxController<T> controller ;

    public LabeledComboBox(ObservableList<T> items, String text) {
        controller = load();
        if (controller != null) {
            setText(text);
            setItems(items);
        }
    }

    public LabeledComboBox(ObservableList<T> items) {
        this(items, "");
    }

    public LabeledComboBox(String text) {
        this(FXCollections.observableArrayList(), text);
    }

    public LabeledComboBox() {
        this("");
    }

    private LabeledComboBoxController<T> load() {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource(
                    "LabeledComboBox.fxml"));
            loader.setRoot(this);
            loader.load();
            return loader.getController() ;
        } catch (Exception exc) {
            Logger.getLogger("LabeledComboBox").log(Level.SEVERE,
                    "Exception occurred instantiating LabeledComboBox", exc);
            return null ;
        }
    }

    // Expose properties, but just delegate to controller to manage them 
    // (by delegating in turn to the underlying controls):

    public void setText(String text) {
        controller.setText(text);
    }
    public String getText() {
        return controller.getText();
    }
    public StringProperty textProperty() {
        return controller.textProperty();
    }

    public boolean isWrapText() {
        return controller.isWrapText();
    }

    public void setWrapText(boolean wrapText) {
        controller.setWrapText(wrapText);
    }

    public BooleanProperty wrapTextProperty() {
        return controller.wrapTextProperty();
    }

    public ObservableList<T> getItems() {
        return controller.getItems();
    }
    public void setItems(ObservableList<T> items) {
        controller.setItems(items);
    }
    public SingleSelectionModel<T> getSelectionModel() {
        return controller.getSelectionModel();
    }
}
public类LabeledComboBox扩展VBox{…}
控制器:

public class LCBController implements Initializable {
    /*Stuff...*/

    /**
     * Set the ComboBox selected value.
     * @param <T> 
     * @param Value
     */
    public <T> void setValue(T Value){
        this.cbx.setValue(Value);
    }

    /**
     * Adds a single item of type T to the ComboBox.
     * @param <T> ComboBox Type
     * @param Item
     */
    public <T> void Add(T Item){
        this.cbx.getItems().add(Item);
    }

    /**
     * Adds a list of items of type T to the ComboBox.
     * @param <T> ComboBox Type
     * @param Items
     */
    public <T> void Add(ObservableList<T> Items){
        this.cbx.getItems().addAll(Items);
    }

    /**
     * Removes an item of type T from the ComboBox.
     * @param <T> ComboBox Type
     * @param Item
     * @return True if successful(?)
     */
    public <T> boolean Remove(T Item){
        return this.cbx.getItems().remove(Item);
    }
}
package application;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.SingleSelectionModel;

public class LabeledComboBoxController<T> {
    @FXML
    private Label label ;
    @FXML
    private ComboBox<T> comboBox ;

    public void setText(String text) {
        label.setText(text);
    }
    public String getText() {
        return label.getText();
    }
    public StringProperty textProperty() {
        return label.textProperty();
    }

    public ObservableList<T> getItems() {
        return comboBox.getItems();
    }
    public void setItems(ObservableList<T> items) {
        comboBox.setItems(items);
    }

    public boolean isWrapText() {
        return label.isWrapText();
    }

    public void setWrapText(boolean wrapText) {
        label.setWrapText(wrapText);
    }

    public BooleanProperty wrapTextProperty() {
        return label.wrapTextProperty();
    }

    public SingleSelectionModel<T> getSelectionModel() {
        return comboBox.getSelectionModel();
    }
}
公共类LCBController实现可初始化{
/*东西*/
/**
*设置组合框的选定值。
*@param
*@param值
*/
公共无效设置值(T值){
此.cbx.setValue(值);
}
/**
*将类型为T的单个项添加到组合框中。
*@param组合框类型
*@param项目
*/
公共作废新增(T项){
this.cbx.getItems().add(Item);
}
/**
*将T类型的项目列表添加到组合框中。
*@param组合框类型
*@param项目
*/
公共作废添加(可观察列表项){
this.cbx.getItems().addAll(Items);
}
/**
*从组合框中删除T类型的项。
*@param组合框类型
*@param项目
*@如果成功返回True(?)
*/
公共布尔删除(T项){
返回此.cbx.getItems().remove(项);
}
}

这样行吗?这条路对吗?同样,我的愿望不过是一个带有标签的组合框,告诉用户它的全部内容。

我确信这种构造是不可能的,因为FXML是在运行时评估的。和泛型已在运行时删除

但是可以做的是为控制器分配一个泛型

FXML实现了模型视图控制器(MVC)设计,该设计受以下主题约束:


您的问题也是以下主题中的一个问题:


这对我很有效,当我将库导入到SceneBuilder中时,效果很好:

(非常基本)FXML:


控制器:

public class LCBController implements Initializable {
    /*Stuff...*/

    /**
     * Set the ComboBox selected value.
     * @param <T> 
     * @param Value
     */
    public <T> void setValue(T Value){
        this.cbx.setValue(Value);
    }

    /**
     * Adds a single item of type T to the ComboBox.
     * @param <T> ComboBox Type
     * @param Item
     */
    public <T> void Add(T Item){
        this.cbx.getItems().add(Item);
    }

    /**
     * Adds a list of items of type T to the ComboBox.
     * @param <T> ComboBox Type
     * @param Items
     */
    public <T> void Add(ObservableList<T> Items){
        this.cbx.getItems().addAll(Items);
    }

    /**
     * Removes an item of type T from the ComboBox.
     * @param <T> ComboBox Type
     * @param Item
     * @return True if successful(?)
     */
    public <T> boolean Remove(T Item){
        return this.cbx.getItems().remove(Item);
    }
}
package application;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.SingleSelectionModel;

public class LabeledComboBoxController<T> {
    @FXML
    private Label label ;
    @FXML
    private ComboBox<T> comboBox ;

    public void setText(String text) {
        label.setText(text);
    }
    public String getText() {
        return label.getText();
    }
    public StringProperty textProperty() {
        return label.textProperty();
    }

    public ObservableList<T> getItems() {
        return comboBox.getItems();
    }
    public void setItems(ObservableList<T> items) {
        comboBox.setItems(items);
    }

    public boolean isWrapText() {
        return label.isWrapText();
    }

    public void setWrapText(boolean wrapText) {
        label.setWrapText(wrapText);
    }

    public BooleanProperty wrapTextProperty() {
        return label.wrapTextProperty();
    }

    public SingleSelectionModel<T> getSelectionModel() {
        return comboBox.getSelectionModel();
    }
}
包应用;
导入javafx.beans.property.BooleanProperty;
导入javafx.beans.property.StringProperty;
导入javafx.collections.ObservableList;
导入javafx.fxml.fxml;
导入javafx.scene.control.ComboBox;
导入javafx.scene.control.Label;
导入javafx.scene.control.SingleSelectionModel;
公共类LabeledBoxController{
@FXML
自有品牌;
@FXML
专用组合框组合框;
公共void setText(字符串文本){
label.setText(文本);
}
公共字符串getText(){
返回label.getText();
}
公共字符串属性textProperty(){
返回label.textProperty();
}
公共可观察列表getItems(){
返回comboBox.getItems();
}
公共无效集合项(可观察列表项){
comboBox.setItems(项目);
}
公共布尔值isWrapText(){
返回标签。isWrapText();
}
公共无效setWrapText(布尔wrapText){
label.setWrapText(wrapText);
}