S.Pro
S.Pro

Reputation: 634

JavaFX-8 TableView with Buttons renders too many of them?

I want to create a TableView with Buttons in each row to delete the specific row. Therefore I created a class extending TableCell with a Button in it. So I set the CellFactory of the column like this:

clmRemoveButtons.setCellFactory(c -> new RemovingCell(clients));

Everything works fine. I'm able to delete the rows and the buttons are displayed correctly, but there is one problem. There are buttons in the whole column. It doesn't matter how many items are in the ObservableList of data. The buttons in rows with items work correctly, but when I click on a button out of this range I get a IndexOutOfBoundsException (that's correct, because there is no data to remove at this point).

So my question is, what do I do wrong and how can I avoid this issue?

best regards

EDIT: Code of RemovingCell (Note: HoverButton is a control extending JavaFX Button with some settings (size etc.), so nothing special.

public class RemovingCell extends TableCell<Client, Client> {
private HoverButton hb = new HoverButton(Color.RED);

public RemovingCell(ObservableList<Client> data) {
    super();
    setAlignment(Pos.CENTER);
    try {
        ImageView imgv = new ImageView(new Image(new FileInputStream(
                "img/Remove.png")));
        imgv.setFitWidth(15);
        imgv.setPreserveRatio(true);
        imgv.setSmooth(true);
        hb.setGraphic(imgv);
        hb.setTooltip(ControlFactory.getTooltip("Klient entfernen"));
        hb.setOnAction(event -> {
            data.remove(getTableRow().getIndex());
        });
        setGraphic(hb);
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    }
}

Upvotes: 0

Views: 475

Answers (1)

James_D
James_D

Reputation: 209225

TableViews that are larger than needed to contain all the data in the table fill the extra space with empty cells. Your TableCell needs to check whether or not the cell is empty before deciding whether or not to include a button in there. You can do this with a listener on the cell's emptyProperty(), or a binding to it:

public class RemovingCell extends TableCell<Client, Client> {
private HoverButton hb = new HoverButton(Color.RED);

public RemovingCell(ObservableList<Client> data) {
    super();
    setAlignment(Pos.CENTER);
    try {
        ImageView imgv = new ImageView(new Image(new FileInputStream(
                "img/Remove.png")));
        imgv.setFitWidth(15);
        imgv.setPreserveRatio(true);
        imgv.setSmooth(true);
        hb.setGraphic(imgv);
        hb.setTooltip(ControlFactory.getTooltip("Klient entfernen"));
        hb.setOnAction(event -> {
            data.remove(getTableRow().getIndex());
        });

        // conditionally set the graphic:
        // setGraphic(hb);

        emptyProperty().addListener( (obs, wasEmpty, isNowEmpty) -> {
            if (isNowEmpty) {
                setGraphic(null);
            } else {
                setGraphic(hb);
            }
        });

        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    }
}

With a binding you can do

graphicProperty().bind(Bindings.when(emptyProperty())
    .then((Node)null)
    .otherwise(hb));

instead of adding the listener to the emptyProperty(). The choice between the two is just a matter of style.

Upvotes: 2

Related Questions