【问题标题】:JavaFX8 TableView with custom control带有自定义控件的 JavaFX8 TableView
【发布时间】:2014-04-09 09:17:10
【问题描述】:

我想在我的 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);
    }
}
}

在主程序中,我使用以下代码填充表格:

        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);

表中的数据来自一个 ObservableList 并且初始化正确。

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

如何将 Client 对象正确放入自定义控件中?

谢谢!

编辑

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());
}

setClient 方法是一个简单的setter 方法(this.client = client;)。变量客户端和建议的定义很简单:

    private ObservableList<Client> suggestions;
private Client client;

【问题讨论】:

    标签: java custom-controls tableview javafx-8


    【解决方案1】:

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

    但随后您需要覆盖一个或多个其他方法,例如 updateItem,以从当前项目中获取数据以进行渲染。

    编辑

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

    @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> client = new SimpleObjectProperty<>(this, "client");
    public final Client getClient(){
        return clientProperty().get();
    }
    public final void setClient(Client client){
        clientProperty().set(client);
    }
    
    public ObjectProperty<Client> clientProperty(){
        return client;
    }
    

    并在构造函数中:监听该属性的变化以设置 labelText 等。 您可能还想提供一个没有客户端参数的构造函数,因为在 TableCell 构造函数中实例化它时它不可用。

    【讨论】:

    • updateItem 方法调用 setGraphic 方法,就像在我使用的另一个自定义单元格中一样,但在这里它不起作用。
    • @SaschaProschinger 你的 NewClientCell 类只定义了一个构造函数而不是 updateItem 方法......!?
    • 对不起,我的意思是我添加了 updateItem 方法,就像我在另一个控件中所做的那样。请参阅已编辑的问题。
    • 嗯,这是一条重要的信息。请参阅我的更新答案。
    • 我以这种方式更新了 updateItem 方法,但没有帮助。经过一些测试,我看到在初始化时 ClientCell 没有得到任何客户端,所以“this.getItem()”返回 null。但是列表中有很多客户。
    【解决方案2】:

    所以我找到了解决问题的方法。非常感谢普斯的帮助! :-) 现在我通过这样的属性设置客户端:

        private ObjectProperty<Client> clientProperty = new SimpleObjectProperty<Client>();
    

    另外我在ClientControl的构造函数中添加了一个ChangeListener:

        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();
    }
    

    由于 ClientControl 中的更改,我的 ClientCell 类只需要一些简单的更改:

    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);
        }
    }
    }
    

    在主程序中没有任何变化。

    最后,我想再次感谢 Puce,我在这个问题上坚持了很多天......

    【讨论】:

    • 使用new ChangeListener&lt;Client&gt;代替new ChangeListener&lt;Object&gt;,然后你可以直接使用newValue参数代替clientProperty.get()
    • 我建议遵循我提到的命名约定...(只有返回 JavaFX 属性对象的方法称为 clientProperty)。
    • 并从 NewClientCell 的构造函数中删除行 cc.setClientProperty(this.getItem());,因为该项目仍然为空。
    • 在 updateItem 方法中,您在 if 和 else 子句中做同样的事情...
    • 感谢您的反馈。我也更新了答案中的代码(只是一个快速的“n”脏解决方案)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2014-03-05
    • 2012-11-19
    • 2014-03-27
    相关资源
    最近更新 更多