Vogel612
Vogel612

Reputation: 5647

Customizing a TableView to have an "Add" button

I have a TableView in my fxml as container for a complex POJO.

I have successfully customized the display to give me a nice Pane for each instance of said complex POJO, but now I want to edit the underlying collection.

For that purpose I could just add a Button in my view with an action-listener and yadda yadda.

But that's a bit incoherent and I'd prefer to have the add-button directly at my TableView. Preferrably in something like a "title bar" on the right side.

I did something similar with a GridPane, but don't want to restructure my code completely so here's a screencap of the effect I'd like to achieve:

The topmost row would be a Table caption, and accordingly styled a bit differently. Am I missing something terribly simple?

Upvotes: 0

Views: 145

Answers (1)

Omid
Omid

Reputation: 6123

This answer is based on this gist:

For the + button in header you just need to create a TableColumn and set its graphic to a button.

Button addButton = new Button("+"); 
TableColumn<Person, Object> buttonColumn = new TableColumn<>();
buttonColumn.setGraphic(addButton);

For - buttons in every row, you need to customize the updateItem method of TableCell class to allow buttons in each row. In order to handle each row's button's action we use a CallBack:

public class ButtonTableCell<S,T> extends TableCell<S,T> {

    private Button button;

    public ButtonTableCell(final Callback<Integer, Void> pressedCallback) {
        this(pressedCallback, null, null);
    }

    public ButtonTableCell(final Callback<Integer, Void> pressedCallback, String buttonText, Node buttonGraphic) {
        this.button = new Button(buttonText, buttonGraphic);
        this.button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                pressedCallback.call(getTableRow().getIndex());
            }
        });
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if(empty) {
            setGraphic(null);
        } else {
            setGraphic(button);
            button.disableProperty().bind(Bindings.not(
                    getTableView().editableProperty().and(
                    getTableColumn().editableProperty()).and(
                    editableProperty())
                ));
        }
    }
}

And then make the buttoned column's CellFactory to use this modified TableCell:

public class TableViewWithButtonColumnDemo extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        TableView<Person> tableview = new TableView<>(
            FXCollections.observableArrayList(
                new Person("Person", "1"),
                new Person("Person", "2"),
                new Person("Person", "3")));

        TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
        firstNameColumn.setCellValueFactory(new PropertyValueFactory("firstName"));
        TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
        lastNameColumn.setCellValueFactory(new PropertyValueFactory("lastName"));

        Button addButton = new Button("+");
        addButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                tableview.getItems().add(new Person("person", String.valueOf(tableview.getItems().size()+1)));
            }
        });

        TableColumn<Person, Object> buttonColumn = new TableColumn<>();
        buttonColumn.setGraphic(addButton);
        buttonColumn.setCellFactory(new Callback<TableColumn<Person,Object>, TableCell<Person,Object>>() {
            @Override
            public TableCell<Person, Object> call(TableColumn<Person, Object> param) {
                Callback<Integer, Void> pressedCallback = new Callback<Integer, Void>() {
                    @Override
                    public Void call(Integer index) {
                        Person buttonPressedPerson = tableview.getItems().get(index);
                        tableview.getItems().remove(buttonPressedPerson);
                        return null;
                    }
                };
                return new ButtonTableCell<>(pressedCallback, "-", null);
            }
        });

        tableview.setEditable(true);
        tableview.getColumns().addAll(firstNameColumn, lastNameColumn, buttonColumn);

        primaryStage.setScene(new Scene(tableview));
        primaryStage.show();
    }

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

Result:

enter image description here

Upvotes: 1

Related Questions