Updating TableView row appearance

2019-01-11 23:27发布

I'm having some dificulties to change the appearance of some TableView rows. The line should show the text with a stroke and in red. Actually, I can show it in red color but still can't do the stroke. This is the css class I'm using to change the appearance of the line:

.itemCancelado {
    -fx-strikethrough: true;
    -fx-text-fill: red;
}

This style class is added when the user mark the item as canceled:

public class ItemCanceladoCellFactory implements Callback<TableColumn, TableCell> {
    @Override
    public TableCell call(TableColumn tableColumn) {
        return new TableCell<ItemBean, Object>() {
            @Override
            public void updateItem(Object item, boolean empty) {
                super.updateItem(item, empty);
                setText(empty ? "" : getItem().toString());
                setGraphic(null);
                int indice=getIndex();
                ItemBean bean=null;
                if(indice<getTableView().getItems().size())
                    bean = getTableView().getItems().get(indice);
                if (bean != null && bean.isCancelado())
                    getStyleClass().add("itemCancelado");
            }
        };
    }
}

There is another problem here, the row marked as canceled only changes the color when the user adds or removes an element from observable list. Is there a way I can force the update of the TableView?

EDITED INFORMATION

I changed the ItemBean class to use BooleanProperty and it solved partially:

public class ItemBean {
    ...
    private BooleanProperty cancelado = new SimpleBooleanProperty(false);
    ...
    public Boolean getCancelado() {
        return cancelado.get();
    }

    public void setCancelado(Boolean cancelado){
        this.cancelado.set(cancelado);
    }

    public BooleanProperty canceladoProperty(){
        return cancelado;
    }
}

Unfortunately, only the column "cancelado" (that will be hided or removed when this finally work) changes the appearance:

cancelado column changes the appearance

here I configure the columns and the table:

public class ControladorPainelPreVenda extends ControladorPainel {

    @FXML
    private TableView<ItemBean> tabelaItens;
    private ObservableList<ItemBean> itens = FXCollections.observableArrayList();
    ...

    private void configurarTabela() {
        colunaCodigo.setCellValueFactory(new MultiPropertyValueFactory<ItemBean, String>("produto.id"));
        colunaCodigo.setCellFactory(new ItemCanceladoCellFactory());
        colunaDescricao.setCellValueFactory(new MultiPropertyValueFactory<ItemBean, String>("produto.descricao"));
        colunaDescricao.setCellFactory(new ItemCanceladoCellFactory());
        colunaLinha.setCellValueFactory(new MultiPropertyValueFactory<ItemBean, String>("produto.nomeLinha"));
        colunaLinha.setCellFactory(new ItemCanceladoCellFactory());
        colunaQuantidade.setCellValueFactory(new PropertyValueFactory<ItemBean, BigDecimal>("quantidade"));
        colunaQuantidade.setCellFactory(new ItemCanceladoCellFactory());
        colunaValorLiquido.setCellValueFactory(new PropertyValueFactory<ItemBean, BigDecimal>("valorLiquido"));
        colunaValorLiquido.setCellFactory(new ItemCanceladoCellFactory());
        colunaValorTotal.setCellValueFactory(new PropertyValueFactory<ItemBean, BigDecimal>("valorTotal"));
        colunaValorTotal.setCellFactory(new ItemCanceladoCellFactory());
        colunaCancelado.setCellValueFactory(new PropertyValueFactory<ItemBean, Boolean>("cancelado"));
        colunaCancelado.setCellFactory(new ItemCanceladoCellFactory());
        tabelaItens.setItems(itens);
    }
    ...
}

How can I update all columns?

3条回答
等我变得足够好
2楼-- · 2019-01-12 00:03

How can I update all columns? If you wish to strike-out entire row, i.e. all cells in a row, every other CellValueFactory should have that condition check:

    ...
    if (indice < getTableView().getItems().size())
        bean = getTableView().getItems().get(indice);
    if (bean != null && bean.isCancelado())
        getStyleClass().add("itemCancelado");
    ...

Or you could implement your callbacks as decorators and have something like this:

public class CellDecorator implements Callback<TableColumn, TableCell> {
    private Callback<TableColumn, TableCell> decorated;

    public CellDecorator(Callback<TableColumn, TableCell> toDecorate) {
      this.decorated = toDecorate;
    }

    // Override this to do your thing.
    public abstract void doStyle(TableCell tableCell);

    @Override
    public void style(TableCell tableCell) {
        // Let the decorated styler do its thing.
        decorated.style(tableCell);

        // Now we do our thing.
        doStyle(cell);
    }
}

public class ItemCanceladoCellFactory extends CellDecorator {
    public ItemCanceladoCellFactory(Callback<TableColumn, TableCell> toDecorate) {
      super(toDecorate);
    }

    @Override
    public void doStyle(TableCell tableCell) {
        ...
        if (bean != null && bean.isCancelado())
            getStyleClass().add("itemCancelado");
    }
}

...
colunaCodigo.setCellValueFactory(new ItemCanceladoCellFactory(new MultiPropertyValueFactory<ItemBean, String>("produto.id")));
colunaDescricao.setCellValueFactory(new ItemCanceladoCellFactory(new MultiPropertyValueFactory<ItemBean, String>("produto.descricao")));
colunaDescricao.setCellFactory(new ItemCanceladoCellFactory(new ItemCanceladoCellFactory()));
....

This way you don't have to repeat the "canceled" styling code, and you can apply it to entire row. Please note that this is not JavaFX ready code, this is a general idea.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-01-12 00:05

Is there a way I can force the update of the TableView?

Make the Cancelado a property of the ItemBean class:

private BooleanProperty cancelado = new SimpleBooleanProperty(false);
public BooleanProperty canceladoProperty() { 
  return cancelado;
}

Now the default cell implementation for the list view will listen for changes on the cancelado property and trigger updateItem calls for the relevant list view cell as appropriate.

Note that the name of the function which returns the property is important, it must be canceladoProperty() as JavaFX extends the standard Java Beans member getter and setter pattern for properties.

Property Naming Convention Background

The naming convention for JavaFX property access is demonstrated in this code snippet from the Oracle Using JavaFX Properties and Binding tutorial.

package propertydemo;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

class Bill {

    // Define a variable to store the property
    private DoubleProperty amountDue = new SimpleDoubleProperty();

    // Define a getter for the property's value
    public final double getAmountDue(){return amountDue.get();}

    // Define a setter for the property's value
    public final void setAmountDue(double value){amountDue.set(value);}

     // Define a getter for the property itself
    public DoubleProperty amountDueProperty() {return amountDue;}

}

There is a great overview of the JavaFX Property Architecture on the openfx wiki which details naming conventions for properties and various more advanced usage scenarios like read only properties and lazy properties.

Customizing the Appearance of a Row in a TableView

Futher information and an example of customizing table row appearance based on a boolean row value is provided in this gist sample code.

private TableColumn<Friend, Boolean> makeBooleanColumn(String columnName, String propertyName, int prefWidth) {
  TableColumn<Friend, Boolean> column = new TableColumn<>(columnName);
  column.setCellValueFactory(new PropertyValueFactory<Friend, Boolean>(propertyName));
  column.setCellFactory(new Callback<TableColumn<Friend, Boolean>, TableCell<Friend, Boolean>>() {
    @Override public TableCell<Friend, Boolean> call(TableColumn<Friend, Boolean> soCalledFriendBooleanTableColumn) {
      return new TableCell<Friend, Boolean>() {
        @Override public void updateItem(final Boolean item, final boolean empty) {
          super.updateItem(item, empty);

          // clear any custom styles
          this.getStyleClass().remove("willPayCell");
          this.getStyleClass().remove("wontPayCell");
          this.getTableRow().getStyleClass().remove("willPayRow");
          this.getTableRow().getStyleClass().remove("wontPayRow");

          // update the item and set a custom style if necessary
          if (item != null) {
            setText(item.toString());
            this.getStyleClass().add(item ? "willPayCell" : "wontPayCell");
            this.getTableRow().getStyleClass().add(item ? "willPayRow" : "wontPayRow");
          }
        }
      };
    }
  });
  column.setPrefWidth(prefWidth);
}

Related

The StackOverflow question Background with 2 colors in JavaFX?, provides a similar solution. The discussion in the answer to that question provides further information on caveats and subtleties of table row highlighting in JavaFX (basically, it's really hard to get the psuedo-class styles - focus rings, selected bars, hover feedback, etc - right with custom row styles).

查看更多
萌系小妹纸
4楼-- · 2019-01-12 00:13

You must set the strikethrough to the .text class: ;-)

.itemCancelado {
    -fx-text-fill: red;
}
.itemCancelado .text {
    -fx-strikethrough: true;
}
查看更多
登录 后发表回答