简体   繁体   中英

JavaFX TableView cell set value later

I have a TableView with 4 rows and for each one a custom cell. The first one is just an image, which one I want to update a few times, a string, a string with word wrap and an second image as a button.

// image
tcUrlStatus.setCellValueFactory(new ColumnImageFactory());
tcUrlStatus.setCellFactory(new ColumnCallback());

// one line string
tcUrlName.setCellValueFactory(new ColumnNameFactory());
tcUrlName.setCellFactory(new ColumnCallback());

// two line string
tcUrlDate.setCellValueFactory(new ColumnDateFactory());
tcUrlDate.setCellFactory(new ColumnCallback());

// image as a button
tcDelete.setCellValueFactory(new ColumnDeleteFactory());
tcDelete.setCellFactory(new ColumnCallback());

And here are the custom cells

class ColumnCallback implements Callback<TableColumn<Quartet<Boolean, String, String, String>, Object>, TableCell<Quartet<Boolean, String, String, String>, Object>>{
    @Override
    public TableCell<Quartet<Boolean, String, String, String>, Object> call(TableColumn<Quartet<Boolean, String, String, String>, Object> column) {
        return new ColumnCell();
    }
}

class ColumnImageFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String>, ObservableValue<Object>> {
    @Override
    public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String> data) {
        return new ReadOnlyObjectWrapper<>(data.getValue().getValue0());
    }
}

class ColumnNameFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, String>, ObservableValue<String>> {
    @Override
    public ObservableValue<String> call(TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, String> data) {
        return new ReadOnlyObjectWrapper<>(data.getValue().getValue1());
    }
}

class ColumnDateFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object>, ObservableValue<Object>> {
    @Override
    public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object> data) {
        return new ReadOnlyObjectWrapper<>(data.getValue().getValue2());
    }
}

class ColumnDeleteFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object>, ObservableValue<Object>> {
    @Override
    public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Boolean, String, String, String>, Object> data) {
        return new ReadOnlyObjectWrapper<>(data.getValue().getValue3());
    }
}

class ColumnCell extends TableCell<Quartet<Boolean, String, String, String>, Object> {
    @Override
    protected void updateItem(Object item, boolean empty) {
        super.updateItem(item, empty);

        if (item != null) {
            if (item instanceof Boolean) {
                setText(null);
                Image image;
                AnchorPane pane = new AnchorPane();

                if ((boolean) item) {
                    image = new Image(Main.class.getResourceAsStream("/hourglass.gif"));
                } else {
                    image = new Image(Main.class.getResourceAsStream("/clean.gif"));
                }

                ImageView imageView = new ImageView(image);
                imageView.setFitWidth(30);
                imageView.setY(5);
                imageView.setPreserveRatio(true);
                imageView.setSmooth(true);

                pane.getChildren().add(imageView);

                setGraphic(pane);
            }else {
                if (item instanceof String) {
                    if (item.equals("delete")) {
                        AnchorPane pane = new AnchorPane();

                        Image image = new Image(Main.class.getResourceAsStream("/cross.png"));

                        ImageView imageView = new ImageView(image);
                        imageView.setFitWidth(20);
                        imageView.setY(10);
                        imageView.setPreserveRatio(true);
                        imageView.setSmooth(true);
                        imageView.setCursor(Cursor.HAND);

                        pane.getChildren().add(imageView);

                        imageView.setOnMouseClicked((Event event) -> {
                            Quartet<Boolean, String, String, String> row = this.getTableView().getSelectionModel().getSelectedItem();

                            Controller.localJson.remove(row.getValue1());
                            this.getTableView().getItems().remove(row);
                        });

                        setGraphic(pane);
                    } else {
                        HBox pane = new HBox();
                        Label label = new Label();
                        label.setText((String) item);

                        label.setTextAlignment(TextAlignment.CENTER);

                        if (((String) item).length() < 20) {
                            label.setWrapText(true);
                            label.setAlignment(Pos.CENTER);
                        }

                        pane.setPrefHeight(40);
                        label.setPrefHeight(40);
                        pane.getChildren().add(label);

                        setGraphic(pane);
                    }

                    setText(null);
                }
            }
        } else {
            setText(null);
            setGraphic(null);
        }
    }
}

Now I want to change the Image of the tcUrlStatus column/cell. I already figured out how to get the row or the value of it, but I can't figure out how to set the value or rather change from true to false or false to true for updating the image.

get row and value:

private int getTableRowIndex(String url){ // url is the second column
    int counter = 0;
    for (Object row:tvUrls.getItems()){
        if ((((Quartet) row).getValue1() == url)){
            return counter;
        }
        counter ++;
    }

    return -1;
}

int rowIndex = getTableRowIndex(url);
Object item = tvUrls.getItems().get(rowIndex);

It would be easiest to do this by using javafx properties in your Quartet class, eg assuming the type used for the value0 bean is T :

// TODO: Is using a readonly property really needed here ???
//       if not, use SimpleObjectProperty instead
private final ReadOnlyObjectWrapper<T> value0 = new ReadOnlyObjectWrapper<>();

public T getValue0() {
     return value0.get();
}

// TODO: should the setter really be public ???
public void setValue0(T newValue) {
     value0.set(newValue);
}

public ReadOnlyObjectProperty<T> value0Property() {
    return value0.getReadOnlyProperty();
}

Which allows you to use new PropertyValueFactory<>("value0") instead of your custom cell value factory, and, even more important, means the TableView components will be notified of changes in the Quartet instances.

This way you can simply use

quartetInstance.setValue0(newValue0);

and the cell will get updated.

Using the PropertyValueFactory would have the same effect as using the following cellValueFactory in this case:

class ColumnImageFactory implements Callback<TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String>, ObservableValue<Object>> {
    @Override
    public ObservableValue<Object> call(TableColumn.CellDataFeatures<Quartet<Object, String, String, String>, String> data) {
        return data.getValue().value0Property();
    }
}

If you cannot add javaFX properties to the Quartet class you need some other way of updating the TableView after the change. TableView.refresh() would work (provided you use JavaFX version >= 8u60 ) or writing an adapter in case you have implemented the observer patten in the Quartet class some other way...

I solved it with chaning from the Quartet Class to the SimpleObjectProperty Class.

Init:

tcUrlStatus.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, Boolean>("status"));
tcUrlName.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, String>("url"));
tcUrlDate.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, String>("date"));
tcDelete.setCellValueFactory(new PropertyValueFactory<ColumnCellValue, Boolean>("delete"));

tcUrlStatus.setCellFactory(new ColumnStatusCell());
tcUrlName.setCellFactory(new ColumnStringCell(false));
tcUrlDate.setCellFactory(new ColumnStringCell(true));
tcDelete.setCellFactory(new ColumnDeleteCell());

Adding rows:

tvUrls.getItems().add(new ColumnCellValue(true, url, date));

Updating cells:

ColumnCellValue statusRow = (ColumnCellValue)
tvUrls.getItems().get(rowIndex);

Column classes:

public class ColumnCellValue{
    private final ObjectProperty status;
    private final ObjectProperty url;
    private final ObjectProperty date;
    private final ObjectProperty delete = new SimpleObjectProperty<Boolean>(true);

    ColumnCellValue(Boolean status, String url, String date) {
        this.status = new SimpleObjectProperty<Boolean>(status);
        this.url = new SimpleObjectProperty<String>(url);
        this.date = new SimpleObjectProperty<String>(date);
    }

    public Object getDate() {
        return date.get();
    }

    public ObjectProperty dateProperty() {
        return date;
    }

    public void setDate(Object date) {
        this.date.set(date);
    }

    public Object getDelete() {
        return delete.get();
    }

    public ObjectProperty deleteProperty() {
        return delete;
    }

    public void setDelete(Object delete) {
        this.delete.set(delete);
    }

    public Object getStatus() {
        return status.get();
    }

    public ObjectProperty statusProperty() {
        return status;
    }

    public void setStatus(Object status) {
        this.status.set(status);
    }

    public Object getUrl() {
        return url.get();
    }

    public ObjectProperty urlProperty() {
        return url;
    }

    public void setUrl(Object url) {
        this.url.set(url);
    }
}

class ColumnStatusCell implements Callback<TableColumn<Boolean, Boolean>,TableCell<Boolean, Boolean>>{
    @Override
    public TableCell<Boolean, Boolean> call(TableColumn<Boolean, Boolean> param) {
        AnchorPane pane = new AnchorPane();

        ImageView imageView = new ImageView();
        imageView.setFitWidth(30);
        imageView.setY(5);
        imageView.setPreserveRatio(true);
        imageView.setSmooth(true);

        TableCell<Boolean,Boolean> cell = new TableCell<Boolean,Boolean>(){
            public void updateItem(Boolean item, boolean empty) {
                if(item!=null){
                    Image image;

                    if (item) {
                        image = new Image(Main.class.getResourceAsStream("/hourglass.gif"));
                    } else {
                        image = new Image(Main.class.getResourceAsStream("/clean.gif"));
                    }

                    imageView.setImage(image);
                }
            }
        };

        pane.getChildren().add(imageView);
        cell.setGraphic(pane);

        return cell;
    }
}

class ColumnStringCell implements Callback<TableColumn<String, String>,TableCell<String, String>>{
    private boolean wrap = false;

    ColumnStringCell(boolean wrap){
        this.wrap = wrap;
    }

    @Override
    public TableCell<String, String> call(TableColumn<String, String> param) {
        TableCell<String,String> cell = new TableCell<String,String>(){
            public void updateItem(String item, boolean empty) {
                if(item!=null){
                    Label label = new Label();
                    label.setText(item);
                    label.setPrefHeight(40);
                    label.setTextAlignment(TextAlignment.CENTER);

                    label.setWrapText(wrap);

                    setGraphic(label);
                }
            }
        };

        return cell;
    }
}

class ColumnDeleteCell implements Callback<TableColumn<Boolean, Boolean>,TableCell<Boolean, Boolean>>{
    @Override
    public TableCell<Boolean, Boolean> call(TableColumn<Boolean, Boolean> param) {
        AnchorPane pane = new AnchorPane();

        ImageView imageView = new ImageView();
        imageView.setFitWidth(20);
        imageView.setY(10);
        imageView.setPreserveRatio(true);
        imageView.setSmooth(true);
        imageView.setCursor(Cursor.HAND);

        TableCell<Boolean,Boolean> cell = new TableCell<Boolean,Boolean>(){
            public void updateItem(Boolean item, boolean empty) {
                if (item != null) {
                    Image image = new Image(Main.class.getResourceAsStream("/cross.png"));
                    imageView.setImage(image);
                }
            }
        };

        imageView.setOnMouseClicked((Event event) -> {
            TableView table = (TableView) ((ImageView) event.getSource()).getParent().getParent().getParent().getParent().getParent().getParent().getParent();
            ColumnCellValue row = (ColumnCellValue) (table).getSelectionModel().getSelectedItem();

            Controller.localJson.remove(row.getUrl().toString());
            table.getItems().remove(row);

            table.refresh();
        });

        pane.getChildren().add(imageView);
        cell.setGraphic(pane);

        return cell;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM