David S. David S. - 1 year ago 140
Java Question

JavaFX TableView scrooling

I have my TableView that contains some number data. When i let's say edit value by button, it changes background of the cell to green. That works good when there is not enough rows to make table scrollable. Once table becomes ( or is from beginning ) scrollable, it start act weird. It changes background on edited item but it on one that is not visible until you scroll down as well. However when i debug, code that changes background is called only once. I think it has to do something with that tableview when is scrollable destroys and recreates cells, or maybe i missunderstood something and i'm doing it wrong.

Example:
I've edited second row.
I've edited second row

And when i scrolled down just a little bit, this one had different background as well. ( they both have it same time )
This one got edited as well

Code:
Model class:

public class Bid {

private DoubleProperty value;
private BooleanProperty valueChanged;

public Bid(double val) {
value = new SimpleDoubleProperty();
valueChanged = new SimpleBooleanProperty();
setValue(val);
value.addListener((observable, old, newVal) -> {
System.out
.println(observable.toString() + " changed value from " + old.doubleValue() + " to " + newVal.doubleValue());
valueChanged.setValue(true);
});
}

public double getValue() {
return value.get();
}

public void setValue(double value) {
this.value.set(value);
}

public DoubleProperty valueProperty() {
return value;
}

public boolean isValueChanged() {
return valueChanged.get();
}

public BooleanProperty valueChangedProperty() {
return valueChanged;
}

public void setValueChanged(boolean valueChanged) {
this.valueChanged.set(valueChanged);
}

@Override
public String toString() {
return "Bid{" +
"value=" + value.getValue() +
'}';
}
}


My cell (when i encoutered this problem for first time i thought it is that style removing part, but removing it doesn't help):

public class BidCell extends TextFieldTableCell<Bid, Double> {


private BooleanProperty newExternalValue;

public BidCell() {
super(new StringConverter<Double>() { // converter used for conversion from/to string


@Override
public String toString(Double object) {
return object.toString();
}

@Override
public Double fromString(String string) {
return Double.valueOf(string);
}
});

newExternalValue = new SimpleBooleanProperty();

newExternalValue.addListener((observable, oldValue, newValue) ->
{
if (newValue) {
String old = this.getStyle();
this.setStyle("-fx-background-color: #99ff99");
System.out.println("Color changed");
Thread thread = new Thread(new StyleRemover(this, old, newExternalValue));
thread.setDaemon(true);
thread.start();
}
});
}


@Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (getTableRow() != null) {
if (getTableRow().getItem() != null) {
if (item != null && !empty) {

Bid b = getTableRow().getItem() instanceof Bid ? ((Bid) getTableRow().getItem()) : null;
if (b != null) {
if (b.isValueChanged()) {
System.out.println("Setting newExternalValue to true for cell" + this);
newExternalValue.setValue(true);
b.setValueChanged(false);
}
}
}
}
}
}

class StyleRemover implements Runnable {

private BidCell cell;
private String oldStyle;
BooleanProperty newExternalValue;

public StyleRemover(BidCell cell, String oldStyle, BooleanProperty newExternalValue) {
this.cell = cell;
this.oldStyle = oldStyle;
this.newExternalValue = newExternalValue;
}

@Override
public void run() {
try {
Thread.sleep(3000);
cell.setStyle(oldStyle);
newExternalValue.setValue(false);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}


Grid:

public class Grid {

private TableView<Bid> view;
private TableColumn<Bid, Double> bidStringTableColumn;
ObservableList<Bid> bids;

public TableView<Bid> getView() {
return view;
}

public Grid() {
view = new TableView<>(); // create tableview

bids = FXCollections.observableArrayList(); // fill data for it
bids.addListener((ListChangeListener<Bid>) c -> {
c.next();
if (c.wasAdded()) {
System.out.println("added new value " + c.getAddedSubList().get(0).toString());
}
});
for (double i = 0; i < 10; i += 0.45) {
Bid e = new Bid(i);

bids.add(e);
}
view.setItems(bids);
view.setEditable(true);

bidStringTableColumn = new TableColumn<>("Bid"); // create column with header text "Bid"
bidStringTableColumn.setCellValueFactory(param -> param.getValue().valueProperty()
.asObject()); // value factory for column, determines which property will fill column
bidStringTableColumn.setCellFactory(e -> new BidCell());

view.getColumns().add(bidStringTableColumn); // add column to table
}


public ObservableList<Bid> getBids() {
return bids;
}


EDIT: It is not just about styles, i discovered that if i start editing that cell and scrool down, that one is in editing mode as well. One possible solution might be just somehow disable cells destroying and recreating for scrolling, however i haven't found a way how to do it and i don't think it is even possible. ( i know that it is there for performance, but performance is not critical for us and there won't be millions of rows, just around 100 max)

Answer Source

It took me some time, but i solved it. The most important thing is this sentence

"The tableview and listview cells are reused to render different row data of the source list."

That basically means that you can't set style to cell because when you scroll it is used for different data. That was my mistake.

Simpliest solution was to store style in model ( Bid ) class and then in cell get it from there. In cell i made method

 public void checkStyle() {
    if (getTableRow().getItem() != null) {
      Bid bid = (Bid) getTableRow().getItem();
      this.setDisabled(bid.isDisabled());
      String style = bid.getStyle();
      if (!this.getStyle().equals(style) && getItem() == bid.getValue()) 
          this.setStyle(style);
    }

which i call in updateItem and updateIndex and in the

  newExternalValue.addListener((observable, oldValue, newValue) ->
    {
      checkStyle();
    });
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download