简体   繁体   中英

JavaFX TableView scrolling

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 misunderstood something and I'm doing it wrong.

Example: 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) 这个也被编辑了

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 scroll 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)

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();
    });

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