Search code examples
javauser-interfacejavafxtableview

JavaFX TableView not updating immediately


I have an issue with JavaFX TableView UI update. After I change the observable object, it does not update the UI of TableView. But if I perform a magical ritual of pulling TableView's scroll bar down and up again - it seems to redraw the table and update items in it. Through debugging I've ensured, that the PreferencesSet ArrayList and object are updated correctly.

Here's gif demonstration of what is happening

This is my first time asking a question here, so I could have left out some important info. Feel free to ask me for it. Thank you in advance.

Here's code (I have left out unrelated stuff):

ControllerClass:

public class TestSomethingController implements Initializable {

public TableView<PreferenceValues.PreferencesSet> preferencesTable;
public TableColumn mdColumn;
public TableColumn typeColumn;
public TableColumn tradeColumn;
public TableColumn plastColumn;
public TableColumn capColumn;
public TableColumn multColumn;
public TableColumn sizeColumn;

@Override
public void initialize(URL location, ResourceBundle resources) {
    setNorthPanel();
    setTableColumns();
    fillAllInfo();
}

private void setTableColumns() {
    mdColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, MarketDirection>("md"));
    typeColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, UserOfferType>("type"));
    tradeColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Boolean>("trade"));
    plastColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Long>("plast"));
    capColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Double>("cap"));
    multColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Double>("mult"));
    sizeColumn.setCellValueFactory(new PropertyValueFactory<PreferenceValues.PreferencesSet, Long>("size"));
}      
private void fillAllInfo() {
     preferencesTable.setItems(FXCollections.observableArrayList(CurrentSession.currentUser.getPreferencesList()));
    fillNorthPanel();
}
public void applyClicked(ActionEvent actionEvent) {
    applyNorthPanelChanges();
}
private void applyNorthPanelChanges() {
    PreferenceValues.PreferencesSet preferencesSet = CurrentSession.currentUser.getPreferencesSet(dirChoiceBox.getSelectionModel().getSelectedItem(), offerTypeChoiceBox.getSelectionModel().getSelectedItem());
    preferencesSet.setTrade(tradeCheckBox.isSelected());
    preferencesSet.setPlast(plastSpinner.getValue());
    preferencesSet.setCap(capRateSpinner.getValue());
    preferencesSet.setMult(multSpinner.getValue());
    preferencesSet.setSize(sizeSpinner.getValue());
    preferencesSet.savePreferences();
}

User class:

public class User {

private PreferenceValues preferenceValues;


public PreferenceValues.PreferencesSet getPreferencesSet(MarketDirection md, UserOfferType userOfferType) {
    return preferenceValues.getPreferencesSet(md, userOfferType);
}

public ArrayList<PreferenceValues.PreferencesSet> getPreferencesList() {
    return preferenceValues.getPreferencesList();
}
}

PreferenceValues class:

import java.util.ArrayList;
import java.util.TreeMap;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

public class PreferenceValues {
private Preferences preferences;
private ArrayList<PreferencesSet> preferencesList;
private TreeMap<String, PreferencesSet> preferencesMap;

public PreferenceValues(User user) {
    preferencesList = new ArrayList<>();
    preferencesMap = new TreeMap<>();
    preferences = Preferences.userRoot().node("prefexample" + user.getwmId());
    for (MarketDirection md : MarketDirection.values()) {
        for (UserOfferType userOfferType : UserOfferType.values()) {
            if (userOfferType != UserOfferType.UNDEF) {
                PreferencesSet preferencesSet = new PreferencesSet(md, userOfferType, preferences);
                preferencesList.add(preferencesSet);
                preferencesMap.put(md.toString() + userOfferType.toString(), preferencesSet);
            }
        }
    }
}

protected ArrayList<PreferencesSet> getPreferencesList() {
    return preferencesList;
}

private String getMapKey(MarketDirection md, UserOfferType userOfferType) {
    return md.toString() + userOfferType.toString();
}

protected PreferencesSet getPreferencesSet(MarketDirection md, UserOfferType userOfferType) {
    return preferencesMap.get(getMapKey(md, userOfferType));
}

public void clear() throws BackingStoreException {
    preferences.clear();
}


public class PreferencesSet {
Preferences preferences;

private MarketDirection md;
private UserOfferType type;
private boolean trade;
private int plast;
private double cap;
private double mult;
private int size;


public PreferencesSet(MarketDirection md, UserOfferType type, Preferences preferences) {
    this.md = md;
    this.type = type;
    this.preferences = preferences;
    trade = preferences.node(md.toString()).node(type.toString()).getBoolean("trade", false);
    plast = preferences.node(md.toString()).node(type.toString()).getInt("plast", 222);
    cap = preferences.node(md.toString()).node(type.toString()).getDouble("cap", 333);
    mult = preferences.node(md.toString()).node(type.toString()).getDouble("mult", 1);
    size = preferences.node(md.toString()).node(type.toString()).getInt("size", 15000);
}

public void savePreferences() {
    preferences.node(md.toString()).node(type.toString()).putBoolean("trade", trade);
    preferences.node(md.toString()).node(type.toString()).putInt("plast", plast);
    preferences.node(md.toString()).node(type.toString()).putDouble("cap", cap);
    preferences.node(md.toString()).node(type.toString()).putDouble("mult", mult);
    preferences.node(md.toString()).node(type.toString()).putInt("size", size);
}

public MarketDirection getMd() {
    return md;
}

public UserOfferType getType() {
    return type;
}

public boolean isTrade() {
    return trade;
}

public int getPlast() {
    return plast;
}

public double getCap() {
    return cap;
}

public double getMult() {
    return mult;
}

public int getSize() {
    return size;
}

public void setTrade(boolean trade) {
    this.trade = trade;
}

public void setPlast(int plast) {
    this.plast = plast;
}

public void setCap(double cap) {
    this.cap = cap;
}

public void setMult(double mult) {
    this.mult = mult;
}

public void setSize(int size) {
    this.size = size;
}
}

}

Solution

  • Since the only way for PropertyValueFactory to retrieve the value is using the getter, changes of a property cannot be observed and therefore the update only happens, when the item is associated with a new TableRow.

    Starting with JavaFX 8u60 you can simply call the refresh method of TableView, which will force an update to be executed.


    However the usual way of doing this is by providing access to a property object containing the property value, e.g.

    In PreferencesSet

    private final IntegerProperty plast = new SimpleIntegerProperty(); 
    
    public void setPlast(int plast) {
        this.plast.set(plast);
    }
    
    public int getPlast() {
        return plast.get();
    }
    
    // this method will be used by the PropertyValueFactory
    // and returns a Property which notifies TableView of changes
    public IntegerProperty plastProperty() {
        return plast;
    }
    

    There are other property types for the other data types, see javafx.beans.property package