Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Content appears in empty rows with custom cell factory

The program below works fine if the line value.setCellFactory(NUMBER_CELL_FACTORY); is commented out.

If it is included however, the TableView behaves weirdly* - if you simply copy the code, launch it and click a few times on the + button, you will see content appearing in empty rows at the bottom of the table.

Am I missing anything?

*Tested on JavaFx 8 - b106.

import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TestFX extends Application {

    @Override
    public void start(final Stage stage) throws Exception {
        MyTable table = new MyTable();

        Scene scene = new Scene(table);

        stage.setScene(scene);
        stage.show();
    }

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

    static class MyTable extends AnchorPane {

        private static final Callback NUMBER_CELL_FACTORY = (p) -> new NumberTableCell<>();

        private final Button addRow = new Button("+");
        private final TableView<Item> table = new TableView<>();

        MyTable() {
            super();
            initTableView();
            addRow.setOnMouseClicked((e) -> addItem());
            VBox vbox = new VBox();
            vbox.getChildren().addAll(addRow, table);
            getChildren().add(vbox);
        }

        public void addItem() {
            table.getItems().addAll(new Item());
        }

        private void initTableView() {
            TableColumn<Item, Double> value = new TableColumn<>("Value");
            value.setCellValueFactory(new PropertyValueFactory<>("value"));
            value.setCellFactory(NUMBER_CELL_FACTORY);

            table.getColumns().add(value);
            table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        }
    }

    public static class Item {
        private final DoubleProperty value = new SimpleDoubleProperty();

        public DoubleProperty valueProperty() {
            return value;
        }
    }

    static class NumberTableCell<T> extends TableCell<T, Double> {
        private final Color fill = Color.LIGHTGREY;

        @Override
        protected void updateItem(Double item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) return;
            setText(String.valueOf(item));
            setTextFill(fill);
        }
    }
}
like image 688
assylias Avatar asked Oct 04 '13 13:10

assylias


1 Answers

I tried your sample program on Win7 Java7b108 and it seemed to work fine with the NumberTableCell, yet it still has an issue.

The problem is that cells are reused, so if a cell becomes empty for any reason, you need to clear out any custom values you set in the cell so that it renders like a default empty cell. The code below demonstrates how to do this:

    @Override
    protected void updateItem(Double item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            setTextFill(null);
            return;
        }
        setText(String.valueOf(item));
        setTextFill(fill);
    }

Here is a small update to your program which more easily replicates the issue and demonstrates the fix. If you comment out the setText(null) and setTextFill(null) lines, then press the + button a few time, then start pressing the - button, you will see that the TableView doesn't really reflect the state of the underlying list. But with the null setters in there, everything is properly in sync. It's a gotcha...

import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TestFX extends Application {

    @Override
    public void start(final Stage stage) throws Exception {
        MyTable table = new MyTable();

        System.getProperties().list(System.out);

        Scene scene = new Scene(table);

        stage.setScene(scene);
        stage.show();
    }

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

    static class MyTable extends AnchorPane {

        private static final Callback NUMBER_CELL_FACTORY = (p) -> new NumberTableCell<>();

        private final Button addRow = new Button("+");
        private final Button removeRow = new Button("-");
        private final TableView<Item> table = new TableView<>();

        MyTable() {
            super();
            initTableView();
            addRow.setOnMouseClicked((e) -> addItem());
            removeRow.setOnMouseClicked((e) -> removeItem());
            VBox vbox = new VBox(5);
            vbox.getChildren().addAll(addRow, removeRow, table);
            getChildren().add(vbox);
        }

        public void addItem() {
            table.getItems().addAll(new Item());
        }

        public void removeItem() {
            if (table.getItems().size() > 0) {
                table.getItems().remove(0);
            }
        }

        private void initTableView() {
            TableColumn<Item, Double> value = new TableColumn<>("Value");
            value.setCellValueFactory(new PropertyValueFactory<>("value"));
            value.setCellFactory(NUMBER_CELL_FACTORY);

            table.getColumns().add(value);
            table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        }
    }

    public static class Item {
        private final DoubleProperty value = new SimpleDoubleProperty();

        public DoubleProperty valueProperty() {
            return value;
        }
    }

    static class NumberTableCell<T> extends TableCell<T, Double> {
        private final Color fill = Color.LIGHTGREY;

        @Override
        protected void updateItem(Double item, boolean empty) {
            super.updateItem(item, empty);
            if (empty || item == null) {
                setText(null);
                setTextFill(null);
                return;
            }
            setText(String.valueOf(item));
            setTextFill(fill);
        }
    }
}
like image 57
jewelsea Avatar answered Sep 30 '22 08:09

jewelsea