带有自定义控件的JavaFX8 TableView

带有自定义控件的JavaFX8 TableView,java,custom-controls,tableview,javafx-8,Java,Custom Controls,Tableview,Javafx 8,我想在TableView中使用自定义控件(代码中的ClientControl)。因此,我创建了一个类ClientCell: public class NewClientCell extends TableCell<Client, Client> { private final ClientControl cc; public NewClientCell(ObservableList<Client> suggestions) { c

我想在TableView中使用自定义控件(代码中的ClientControl)。因此,我创建了一个类ClientCell:

    public class NewClientCell extends TableCell<Client, Client> {
    private final ClientControl cc;

    public NewClientCell(ObservableList<Client> suggestions) {
        cc = new ClientControl(this.getItem(), suggestions);
        this.setAlignment(Pos.CENTER_LEFT);
        this.setGraphic(cc);
        this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    }
@Override
protected void updateItem(Client c, boolean empty) {
    super.updateItem(c, empty);
    if(!empty){
        setGraphic(cc);
    }
}
}
公共类NewClientCell扩展了TableCell{
私人最终客户控制cc;
公共NewClientCell(可观察列表建议){
cc=新的ClientControl(this.getItem(),建议);
此.设置对齐(位置中心左);
这是一个设定图形(cc);
此.setContentDisplay(仅限ContentDisplay.GRAPHIC_);
}
@凌驾
受保护的void updateItem(客户端c,布尔值为空){
super.updateItem(c,空);
如果(!空){
设置图形(cc);
}
}
}
在主程序中,我使用以下代码填写表格:

        TableColumn<Client, Client> clmClients = new TableColumn<>("Klient");
    clmClients.setCellFactory(new Callback<TableColumn<Client, Client>, TableCell<Client, Client>>() {
        @Override
        public TableCell<Client, Client> call(TableColumn<Client, Client> p) {
            return new NewClientCell(suggestions);
        };
    });

    clmClients.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Client, Client>, ObservableValue<Client>>() { 
        @Override
        public ObservableValue<Client> call(TableColumn.CellDataFeatures<Client, Client> p) {
            return new SimpleObjectProperty<Client>(p.getValue());
        }
    });
    getColumns().add(clmClients);
TableColumn clmClients=newtableColumn(“Klient”);
setCellFactory(新回调(){
@凌驾
公共TableCell调用(TableP列){
返回新的NewClientCell(建议);
};
});
clmClients.setCellValueFactory(新回调(){
@凌驾
公共observeValue调用(TableColumn.celldatap){
返回新的SimpleObject属性(p.getValue());
}
});
getColumns().add(clmClients);
表中的数据来自ObservableList,并且初始化正确

我现在的问题是,自定义控件需要一个客户端对象,它应该从ObservableList中获取该对象,但是“this.getItem()”始终返回null

如何将客户机对象正确地放入自定义控件中

谢谢

编辑

以下是ClientControl的构造函数:

    public ClientControl(Client client, ObservableList<Client> suggestions) {
    setClient(client);
    setSuggestions(suggestions);
    FXMLLoader loader = new FXMLLoader(getClass().getResource("ClientControl.fxml"));
    loader.setRoot(this);
    loader.setController(this);
    try {
        loader.load();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    initTextField();
    setLabelText(client.toString());
}
    public ClientControl(ObservableList<Client> suggestions) {
    clientProperty.addListener(new ChangeListener<Client>() {
        @Override
        public void changed(ObservableValue<? extends Client> observable, Client oldValue,
                ClientnewValue) {
            if(newValue != null) {
                setLabelText(newValue.toString());
            }
        }
    });
    setSuggestions(suggestions);
    FXMLLoader loader = new FXMLLoader(getClass().getResource("ClientControl.fxml"));
    loader.setRoot(this);
    loader.setController(this);
    try {
        loader.load();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    initTextField();
}
public class NewClientCell extends TableCell<Client, Client> {
private final ClientControl cc;

public NewClientCell(ObservableList<Client> suggestions) {
    cc = new ClientControl(suggestions);
this.setAlignment(Pos.CENTER_LEFT);
this.setGraphic(cc);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}

@Override
protected void updateItem(Client c, boolean empty) {
    super.updateItem(c, empty);
    if(!empty){
        cc.setClient(c);
    }
}
}
公共客户端控制(客户端、可观察列表建议){
setClient(客户机);
设置建议(建议);
FXMLLoader=newFXMLLoader(getClass().getResource(“ClientControl.fxml”);
setRoot(this);
loader.setController(此);
试一试{
loader.load();
}捕获(IOE异常){
抛出新的运行时异常(e);
}
initTextField();
setLabelText(client.toString());
}
方法setClient是一个简单的setter方法(this.client=client;)。客户变量和建议的定义如下:

    private ObservableList<Client> suggestions;
private Client client;
私人可观察列表建议;
私人客户;

AFAIK,您应该像以前一样实例化构造函数中的任何控件,以便它们只创建一次(请记住,单元格会在不同的位置重复使用)

但是,您需要覆盖一个或多个其他方法,例如
updateItem
,以从当前要渲染的项获取数据

编辑

好吧,您分配的是同一个控件,而不是一次又一次地更改它。设置客户端控件的item属性,而不是在updateItem方法中设置图形:

@Override
protected void updateItem(Client c, boolean empty) {
    super.updateItem(c, empty);
    if(!empty){
        cc.setClient(c);
    } else {
        cc.setClient(null);
    }
}
编辑2

ClientControl应将客户端项作为属性而不是构造函数参数提供,并在updateItem方法中设置它,而不是在构造函数中设置它

例如,类似这样的东西(未经测试!):

private final ObjectProperty client=new SimpleObjectProperty(此“客户机”);
公共最终客户端getClient(){
返回clientProperty().get();
}
公共最终作废设置客户端(客户端){
clientProperty().set(客户端);
}
公共对象属性clientProperty(){
返回客户;
}
在构造函数中:侦听此属性的更改以设置labelText等。
您可能还希望提供一个没有客户机参数的构造函数,因为在TableCell构造函数中实例化它时它不可用。

因此我找到了解决问题的方法。非常感谢Puce的帮助!:-) 现在我通过如下属性设置客户端:

    private ObjectProperty<Client> clientProperty = new SimpleObjectProperty<Client>();
private ObjectProperty clientProperty=new SimpleObjectProperty();
此外,我还在ClientControl的构造函数中添加了一个ChangeListener:

    public ClientControl(Client client, ObservableList<Client> suggestions) {
    setClient(client);
    setSuggestions(suggestions);
    FXMLLoader loader = new FXMLLoader(getClass().getResource("ClientControl.fxml"));
    loader.setRoot(this);
    loader.setController(this);
    try {
        loader.load();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    initTextField();
    setLabelText(client.toString());
}
    public ClientControl(ObservableList<Client> suggestions) {
    clientProperty.addListener(new ChangeListener<Client>() {
        @Override
        public void changed(ObservableValue<? extends Client> observable, Client oldValue,
                ClientnewValue) {
            if(newValue != null) {
                setLabelText(newValue.toString());
            }
        }
    });
    setSuggestions(suggestions);
    FXMLLoader loader = new FXMLLoader(getClass().getResource("ClientControl.fxml"));
    loader.setRoot(this);
    loader.setController(this);
    try {
        loader.load();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    initTextField();
}
public class NewClientCell extends TableCell<Client, Client> {
private final ClientControl cc;

public NewClientCell(ObservableList<Client> suggestions) {
    cc = new ClientControl(suggestions);
this.setAlignment(Pos.CENTER_LEFT);
this.setGraphic(cc);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}

@Override
protected void updateItem(Client c, boolean empty) {
    super.updateItem(c, empty);
    if(!empty){
        cc.setClient(c);
    }
}
}
公共客户端控制(可观察列表建议){
clientProperty.addListener(新的ChangeListener(){
@凌驾

更改公众假期(ObservalEvalueUpdateItem方法调用setGraphic方法,就像在我使用的另一个自定义单元格中一样,但在这里它不起作用。@SaschaProschinger您的NewClientCell类只定义了一个构造函数而不是updateItem方法…?对不起,我的意思是我添加了updateItem方法,就像在其他控件中一样。请参阅编辑的问题。这是一个重要的问题nt信息。请参阅我的更新答案。我以这种方式更新了updateItem方法,但没有任何帮助。经过一些测试,我发现在初始化时ClientCell没有获得任何客户端,因此“this.getItem()”返回null。但是列表中有许多客户端。请使用
new-changerensister
而不是
new-changerensister
,然后可以直接使用
newValue
参数而不是
clientProperty.get()
。我建议遵循我提到的命名约定。。。(只有返回JavaFX属性对象的metod称为clientProperty)。并删除行
cc.setClientProperty(this.getItem());
来自NewClientCell的构造函数,因为项仍然为空。在updateItem方法中,您在if and else子句中做了相同的事情…感谢您的反馈。我也更新了答案中的代码(只是一个快速的“n”脏解决方案)