javafx:如何以编程方式更改 TableCell 中 ComboBox 的项目?

javafx : how to programmatically change items of ComboBox in TableCell?

如您所见,我有一个 TableView,其中有两列 ComboBoxTableCell。

这些是祖先值

ObservableList<String> ancestors = FXCollections.observableArrayList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
ancestorColumn.setCellFactory(ComboBoxTableCell.forTableColumn(ancestors));
descendantColumn.setCellFactory(ComboBoxTableCell.forTableColumn());

Map<String, List<String>> descendantMap = new HashMap();
descendantMap.put("1", Arrays.asList("A", "B", "C"));
descendantMap.put("2", Arrays.asList("Z", "L", "C"));
descendantMap.put("3", Arrays.asList("A", "B", "R"));
descendantMap.put("4", Arrays.asList("C", "B", "E"));
descendantMap.put("5", Arrays.asList("A", "E", "C"));
descendantMap.put("6", Arrays.asList("M", "V", "T"));
descendantMap.put("7", Arrays.asList("A", "G", "F"));
descendantMap.put("8", Arrays.asList("J", "O", "N"));
descendantMap.put("9", Arrays.asList("X", "G", "E"));
descendantMap.put("10", Arrays.asList("H", "I", "J"));

现在,当我更改祖先组合框的选择时,相应的后代组合框应该填充该键映射中的项目。即,如果我在 table 行上将祖先更改为“1”,则同一行上的后代组合框应该填充 ("A"、"B"、“C”。如何实现这个?

编辑: 嗨,我已经做到了并且有效。但我不确定这样做是否正确。谁能告诉我这样做是否正确?

descendantColumn.setCellFactory(param -> {
    return new TableCell() {
        ComboBox box = new ComboBox();
        @Override
        public void updateItem(String item, boolean empty) {
            if(!empty) {
                setGraphic(box);
                ancestorColumn.getCellObservableValue(getIndex()).addListener((o, ov, nv) -> {
                    if(nv != null) {
                        box.setItems(descendantMap.get(nv));
                    }
                });
            } else {
                setGraphic(null);
            }
        }
    }; 
});

我已经创建了一个示例来说明我将如何做到这一点。但是请注意:我在半小时左右的时间内构建了这个 - 代码远非完美,好吗?

这是完整的代码...

FXML:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    Do not edit this file it is generated by e(fx)clipse from ../src/application/TableTest.fxgraph
-->

<?import java.lang.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="application.TableTestController">

    <center>
        <TableView fx:id="tableView"> 
            <columns>
                <TableColumn fx:id="tableColumnText" text="Text" prefWidth="150"/> 
                <TableColumn fx:id="tableColumnFlag" text="Flag" prefWidth="50"/> 
                <TableColumn fx:id="tableColumnAncestor" text="Ancestor" prefWidth="75"/> 
                <TableColumn fx:id="tableColumnDescendant" text="Descendant" prefWidth="100"/> 
                <TableColumn fx:id="tableColumnPoints" text="Points" prefWidth="75"/> 
            </columns>
            <BorderPane.margin>
                <Insets top="5" left="5" right="5" bottom="5"/> 
            </BorderPane.margin>
        </TableView>
    </center>
</BorderPane>

模型数据class(表示 TableView 中的一行):

package application;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class TableTestObject {

    private StringProperty text = new SimpleStringProperty("");
    private BooleanProperty flag = new SimpleBooleanProperty(false);
    private StringProperty ancestor = new SimpleStringProperty("");
    private StringProperty descendant = new SimpleStringProperty("");
    private IntegerProperty points = new SimpleIntegerProperty(0);

    public final StringProperty textProperty() {
        return this.text;
    }

    public final String getText() {
        return this.textProperty().get();
    }

    public final void setText(final String text) {
        this.textProperty().set(text);
    }

    public final BooleanProperty flagProperty() {
        return this.flag;
    }

    public final boolean isFlag() {
        return this.flagProperty().get();
    }

    public final void setFlag(final boolean flag) {
        this.flagProperty().set(flag);
    }

    public final StringProperty ancestorProperty() {
        return this.ancestor;
    }

    public final String getAncestor() {
        return this.ancestorProperty().get();
    }

    public final void setAncestor(final String ancestor) {
        this.ancestorProperty().set(ancestor);
    }

    public final StringProperty descendantProperty() {
        return this.descendant;
    }

    public final String getDescendant() {
        return this.descendantProperty().get();
    }

    public final void setDescendant(final String descendant) {
        this.descendantProperty().set(descendant);
    }

    public final IntegerProperty pointsProperty() {
        return this.points;
    }

    public final int getPoints() {
        return this.pointsProperty().get();
    }

    public final void setPoints(final int points) {
        this.pointsProperty().set(points);
    }
}

主要class:

package application;

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

public class Main extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("TableTest.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root, 1200, 800);
        stage.setScene(scene);
        stage.show();
    }

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

TableTestController(做绑定的那个):

package application;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

public class TableTestController {

    @FXML
    private TableView<TableTestObject> tableView;
    @FXML
    private TableColumn<TableTestObject, String> tableColumnText;
    @FXML
    private TableColumn<TableTestObject, TableTestObject> tableColumnFlag;
    @FXML
    private TableColumn<TableTestObject, TableTestObject> tableColumnAncestor;
    @FXML
    private TableColumn<TableTestObject, TableTestObject> tableColumnDescendant;
    @FXML
    private TableColumn<TableTestObject, TableTestObject> tableColumnPoints;

    private Map<String, List<String>> descendantMap;
    private ObservableList<String> ancestorList;

    @FXML
    protected void initialize() {

        descendantMap = new HashMap<>();
        descendantMap.put("1", Arrays.asList("A", "B", "C"));
        descendantMap.put("2", Arrays.asList("Z", "L", "C"));
        descendantMap.put("3", Arrays.asList("A", "B", "R"));
        descendantMap.put("4", Arrays.asList("C", "B", "E"));
        descendantMap.put("5", Arrays.asList("A", "E", "C"));
        descendantMap.put("6", Arrays.asList("M", "V", "T"));
        descendantMap.put("7", Arrays.asList("A", "G", "F"));
        descendantMap.put("8", Arrays.asList("J", "O", "N"));
        descendantMap.put("9", Arrays.asList("X", "G", "E"));
        descendantMap.put("10", Arrays.asList("H", "I", "J"));

        ancestorList = FXCollections.observableArrayList(descendantMap.keySet());

        ObservableList<TableTestObject> data = FXCollections.observableArrayList();
        TableTestObject test = new TableTestObject();
        test.setText("Test");
        test.setFlag(true);
        test.setPoints(1);
        data.add(test);

        tableView.setItems(data);

        tableColumnText.setCellValueFactory(new PropertyValueFactory<>("text"));
        tableColumnFlag.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
        tableColumnAncestor.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
        tableColumnDescendant.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));
        tableColumnPoints.setCellValueFactory(param -> new SimpleObjectProperty<TableTestObject>(param.getValue()));

        tableColumnFlag.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
            @Override
            protected void updateItem(TableTestObject item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText(null);
                } else {
                    CheckBox cb = new CheckBox();
                    cb.selectedProperty().bindBidirectional(item.flagProperty());
                    setGraphic(cb);
                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                }
            }
        });
        tableColumnAncestor.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
            @Override
            protected void updateItem(TableTestObject item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText(null);
                } else {
                    ComboBox<String> cb = new ComboBox<>(ancestorList);
                    cb.getSelectionModel().selectedItemProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
                        item.ancestorProperty().set(newValue);
                        System.out.println(item.descendantProperty().get());
                    });
                    setGraphic(cb);
                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                }
            }
        });
        tableColumnDescendant.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
            @Override
            protected void updateItem(TableTestObject item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText(null);
                } else {
                    ComboBox<String> cb = new ComboBox<>();
                    item.ancestorProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
                        item.descendantProperty().set("");
                        cb.setItems(FXCollections.observableArrayList(descendantMap.get(newValue)));
                    });
                    cb.getSelectionModel().selectedItemProperty().addListener((ChangeListener<String>) (observable, oldValue, newValue) -> {
                        item.descendantProperty().set(newValue);
                        System.out.println(item.descendantProperty().get());
                    });
                    setGraphic(cb);
                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                }
            }
        });
        tableColumnPoints.setCellFactory(param -> new TableCell<TableTestObject, TableTestObject>() {
            @Override
            protected void updateItem(TableTestObject item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText(null);
                } else {
                    Spinner<Integer> spinner = new Spinner<>();
                    spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(-5, +5, item.getPoints()));
                    spinner.valueProperty().addListener((ChangeListener<Integer>) (observable, oldValue, newValue) -> {
                        item.pointsProperty().set(newValue);
                        System.out.println(item.pointsProperty().get());
                    });
                    setGraphic(spinner);
                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                }
            }
        });
    }
}

再说一次:控制器就是执行绑定的那个。使用 Lambdas,代码几乎是可读的,但无论如何您都应该考虑重新编写它。

干杯,丹尼尔