为 TableView 单个单元格设置禁用 属性

Set disable property for TableView individual cells

如何为 TableView 中的单个单元格设置禁用 属性?

这是我的场景:一列有一个 ComboBoxTableCell,并且根据选择的项目,同一行上的一些单元格将被禁用。

例如:

如果我选择类型 A,则输入 A 启用,输入 B 禁用,反之亦然。

禁用是指已清除、变灰且不可编辑。

我的场景的一个最小示例:

TableTest.java

package minimalexample;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TableTest extends Application {

    private TableView itemsTable;

    ObservableList<ItemsTableLine> items;

    @Override
    public void start(Stage primaryStage) {

        items = FXCollections.observableArrayList();
        items.addAll(new ItemsTableLine("A","1","2"),
                     new ItemsTableLine("A","3","4"),
                     new ItemsTableLine("B","5", "6"),
                     new ItemsTableLine());

        ItemsTable itemsTableParent = new ItemsTable();
        itemsTable = itemsTableParent.makeTable(items);

        VBox root = new VBox();        
        root.getChildren().addAll(itemsTable);

        Scene scene = new Scene(root, 200, 100);
        primaryStage.setTitle("Minimal Example");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

ItemsTable.java

package minimalexample;


import javafx.util.Callback;
import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Pos;

import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;

import javafx.collections.ObservableList;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

public class ItemsTable {

    private String lastKey = null;

    // This will be exposed through a getter to be updated from list in LoadsTable
    private TableColumn<ItemsTableLine, ItemType> typeCol;
    private TableColumn<ItemsTableLine, String> inputACol;
    private TableColumn<ItemsTableLine, String> inputBCol;

    public TableView makeTable(ObservableList<ItemsTableLine> items) {

        TableView tv = new TableView(items);
        tv.setEditable(true);

        Callback<TableColumn<ItemsTableLine, String>, TableCell<ItemsTableLine, String>> txtCellFactory
                = (TableColumn<ItemsTableLine, String> p) -> {
                    return new EditingCell();
                };

        ObservableList<ItemType> itemTypeList
                = FXCollections.observableArrayList(ItemType.values());
        typeCol = new TableColumn<>("Type");
        inputACol  = new TableColumn<>("Input A");
        inputBCol   = new TableColumn<>("Input B");

        typeCol.setCellValueFactory(new Callback<CellDataFeatures<ItemsTableLine, ItemType>, ObservableValue<ItemType>>() {

            @Override
            public ObservableValue<ItemType> call(CellDataFeatures<ItemsTableLine, ItemType> param) {
                ItemsTableLine lineItem = param.getValue();
                String itemTypeCode = lineItem.typeProperty().get();
                ItemType itemType = ItemType.getByCode(itemTypeCode);
                return new SimpleObjectProperty<>(itemType);
            }
        });

        inputACol.setCellValueFactory(new PropertyValueFactory<>("inputA"));
        inputBCol.setCellValueFactory(new PropertyValueFactory<>("inputB"));
        typeCol.setCellFactory(ComboBoxTableCell.forTableColumn(itemTypeList));
        inputACol.setCellFactory(txtCellFactory);
        inputBCol.setCellFactory(txtCellFactory);
        typeCol.setOnEditCommit((CellEditEvent<ItemsTableLine, ItemType> event) -> {
            TablePosition<ItemsTableLine, ItemType> pos = event.getTablePosition();
            ItemType newItemType = event.getNewValue();
            int row = pos.getRow();
            ItemsTableLine lineItem = event.getTableView().getItems().get(row);
            lineItem.setType(newItemType.getCode());
        });
        inputACol.setOnEditCommit((TableColumn.CellEditEvent<ItemsTableLine, String> evt) -> {
            evt.getTableView().getItems().get(evt.getTablePosition().getRow())
                    .inputAProperty().setValue(evt.getNewValue().replace(",", "."));
        });
        inputBCol.setOnEditCommit((TableColumn.CellEditEvent<ItemsTableLine, String> evt) -> {
            evt.getTableView().getItems().get(evt.getTablePosition().getRow())
                    .inputBProperty().setValue(evt.getNewValue().replace(",", "."));
        });


        tv.getColumns().setAll(typeCol, inputACol, inputBCol);

        return tv;
    }

    private class EditingCell extends TableCell {
        private TextField textField;
        @Override public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                createTextField();
                setText(null);
                setGraphic(textField);
                //setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
                Platform.runLater(() -> {//without this space erases text, f2 doesn't
                    textField.requestFocus();//also selects
                });
                if (lastKey != null) {
                    textField.setText(lastKey);
                    Platform.runLater(() -> {textField.deselect(); textField.end();});}
            }
        }
        public void commit() { commitEdit(textField.getText()); }

        @Override
        public void cancelEdit() {
            super.cancelEdit();
            try {
                setText(getItem().toString());
            } catch (Exception e) {
            }
            setGraphic(null);
        }

        @Override
        public void updateItem(Object item, boolean empty) {
            super.updateItem(item, empty);

            if (empty) {
                setText(null);
                setGraphic(null);
            } else if (isEditing()) {
                if (textField != null) {
                    textField.setText(getString());
                }
                setText(null);
                setGraphic(textField);
            } else {
                setText(getString());
                setGraphic(null);
                if (!getTableColumn().getText().equals("Name")) {
                    setAlignment(Pos.CENTER);
                }
            }
        }
        private void createTextField() {
            textField = new TextField(getString());
            textField.focusedProperty().addListener(
                    (ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> {
                        if (!arg2) { commitEdit(textField.getText()); }});
            textField.setOnKeyReleased((KeyEvent t) -> {
                if (t.getCode() == KeyCode.ENTER) {
                    commitEdit(textField.getText());
                    EditingCell.this.getTableView().getSelectionModel().selectBelowCell(); }
                if (t.getCode() == KeyCode.ESCAPE) { cancelEdit(); }});
            textField.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent t) -> { 
                if (t.getCode() == KeyCode.DELETE) { t.consume();}});
        }
        private String getString() {return getItem() == null ? "" : getItem().toString();}
    }
}

ItemsTableLine.java

package minimalexample;
import java.io.Serializable;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class ItemsTableLine implements Serializable {

    private StringProperty type;
    private StringProperty inputA;
    private StringProperty inputB;

    public StringProperty typeProperty() {return type;}
    public StringProperty inputAProperty()  {return inputA;  }
    public StringProperty inputBProperty()   {return inputB;}

    public ItemsTableLine() {
        super();
        type = new SimpleStringProperty("");
        inputA  = new SimpleStringProperty("");
        inputB   = new SimpleStringProperty("");
    }

    public ItemsTableLine(String...values) {
        this();
        type.set(values[0]);
        inputA.set(values[1]);
        inputB.set(values[2]);
    }

    // Setters required due to combobox 
    public void setType(String value) {
        type.set(value);
    }
}

ItemsType.java

package minimalexample;

public enum ItemType {
    TYPEA("A", "Type A"),
    TYPEB("B", "Type B");
    private String code;
    private String text;
    private ItemType(String code, String text) { this.code = code; this.text = text;}
    public String getCode() { return code; }
    public String getText() { return text; }
    public static ItemType getByCode(String genderCode) {
       for (ItemType g : ItemType.values()) if (g.code.equals(genderCode)) return g;
       return null;
    }
    @Override public String toString() { return this.text; }
}

代码需要一些重构,但您可以通过执行以下操作实现您的要求:

inputACol.setCellFactory(param -> new EditingCell() {
    @Override
    public void updateItem(Object item, boolean empty) {
        super.updateItem(item, empty);
        if (item != null && !empty) {
            ItemsTableLine data = (ItemsTableLine) getTableView().getItems().get(getIndex());
            disableProperty().bind(Bindings.createBooleanBinding(() ->
                    !data.typeProperty().get().equals("A"), data.typeProperty()));
        } else {
            disableProperty().unbind();
        }
    }
});
inputBCol.setCellFactory(param -> new EditingCell() {
    @Override
    public void updateItem(Object item, boolean empty) {
        super.updateItem(item, empty);
        if (item != null && !empty) {
            ItemsTableLine data = (ItemsTableLine) getTableView().getItems().get(getIndex());
            disableProperty().bind(Bindings.createBooleanBinding(() ->
                    !data.typeProperty().get().equals("B"), data.typeProperty()));
        } else {
            disableProperty().unbind();
        }
    }
});

注意:有不止一种方法可以做到这一点,但在您的情况下这似乎是最简单的方法。

注意:除了绑定中使用的值外,这两个代码块完全相同,您可以编写一个方法,将其作为输入并 returns 一个单元格工厂而不是代码重复。