TableView Live Javafx

I have one tableview with JafavaFx for show to the user currencies each second. Every thing works ok but when the tableView try to reload the data it stop something that milliseconds, i mean freezer and looks not very nive. I couldn't remove this behavior the code that I use in this moment is that:

setCache(true);

        setItems(getObservableList());
        Timeline animation = new Timeline();
        animation.getKeyFrames().add(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                setItems(getObservableList());                  
            }
        }));
        animation.setCycleCount(Animation.INDEFINITE);
        animation.play();

My TableColum

getColumns().addAll(
            getStringField("id","Tikect"), 
            getStringField("currency","Symbol"), 
            getStringField("type","S/B"), 
            getDoubleField("amount","Amt (k)"), 
            getDoubleField("open","Open"), 
            getDoubleField("close","Close"),
            getDoubleField("stop","Stop"), 
            getDoubleField("limit","Limit"), 
            getDoubleField("pl","P/L"), 
            getDoubleField("gpl","Gross P/L")
            );

In other class for add the columns I have one function like that:

    public static TableColumn<BaseModel, String> getStringField(String idColumn,String nameColumn) {
    TableColumn<BaseModel, String> column = new TableColumn<BaseModel, String>(idColumn);
    column.setCellValueFactory(new PropertyValueFactory<BaseModel, String>(idColumn));
    column.setMinWidth(100);
    column.setText(nameColumn);

    return column;
}

My ObservableList is :

public ObservableList<BaseModel> getObservableList() throws IOException {
    ObservableList<BaseModel> oTransacctions = FXCollections.observableArrayList();

    JsonFactory factory = new JsonFactory();
    JsonParser jp = factory.createJsonParser(new URL(getURL()));

    JsonToken token = jp.nextToken();

    while (token != JsonToken.START_ARRAY) {
        token = jp.nextToken();
    }

    while (token != JsonToken.END_ARRAY) {
        token = jp.nextToken();

        if (token == JsonToken.START_OBJECT) {
            Transaction transaction = parseTransacction(jp);
            oTransacctions.add(transaction);
        }
    }

    return oTransacctions;
}

private Transaction parseTransacction(JsonParser jp) throws IOException {
    Transaction transaction = new Transaction();
    JsonToken token = jp.nextToken();

    while (token != JsonToken.END_OBJECT) {
        if (token == JsonToken.START_OBJECT) {
            while (token != JsonToken.END_OBJECT) {
                token = jp.nextToken();
            }
        }

        if (token == JsonToken.FIELD_NAME) {
            String fieldname = jp.getCurrentName();

            if ("id".equals(fieldname)) {
                jp.nextToken();
                transaction.setId(jp.getText());
            }
            if ("currency".equals(fieldname)) {
                jp.nextToken();
                transaction.setCurrency(jp.getText());
            }
            if ("type".equals(fieldname)) {
                jp.nextToken();
                transaction.setType(jp.getText());
            }
            if ("amount".equals(fieldname)) {
                jp.nextToken();
                transaction.setAmount(jp.getValueAsDouble());
            }
            if ("open".equals(fieldname)) {
                jp.nextToken();
                transaction.setOpen(jp.getValueAsDouble());
            }
            if ("stop".equals(fieldname)) {
                jp.nextToken();
                transaction.setStop(jp.getValueAsDouble());
            }
            if ("limit".equals(fieldname)) {
                jp.nextToken();
                transaction.setLimit(jp.getValueAsDouble());
            }
            if ("created_at".equals(fieldname)) {
                jp.nextToken();
                // transaction.setCreated_at(new Date(jp.getText()));
            }
            if ("pl".equals(fieldname)) {
                jp.nextToken();
                transaction.setPl(jp.getValueAsDouble());
            }
            if ("gpl".equals(fieldname)) {
                jp.nextToken();
                transaction.setGpl(jp.getValueAsDouble());
            }
            if ("close".equals(fieldname)) {
                jp.nextToken();
                transaction.setClose(jp.getDoubleValue());
            }
        }
        token = jp.nextToken();
    }

    return transaction;
}

and this is the result :

enter image description here

My questions is :

Thanks a lot !

Upvotes: 3

Views: 2637

Answers (2)

jewelsea
jewelsea

Reputation: 159616

It Looks like you are doing I/O in some of your methods (at least that have a throws IOException in their signature).

You should not do any I/O on the JavaFX Application Thread as that thread processes the user interface. If you do perform I/O on the JavaFX Application Thread, then you will end up blocking the thread and the program will appear unresponsive to the user.

What you want to do is fetch the data from the server to the client asynchronously. To do that you should make use of the JavaFX concurrency utilities such as Task and Service. One other thing to be careful of is that your I/O fetching thread shouldn't directly update your UI (or even indirectly the ObservableList backing your table).

In your case what I would do something like the code below:

final ForexService forex = new ForexService();
forex.setUrl(getUrl()); // getUrl() is some method your application provides 
forex.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
  @Override public void handle(WorkerStateEvent event) {
    setItems(forex.getValue());     
    forex.reset();             
  }
}           
forex.setOnFailed(new EventHandler<WorkerStateEvent>() {
  @Override public void handle(WorkerStateEvent event) {
    // exception handling with forex.getException()   
    forex.reset();             
  }
}           
...
Timeline animation = new Timeline();
animation.getKeyFrames().add(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
  @Override public void handle(WorkerStateEvent event) {
    if (Worker.State.READY == forex.getState()) {
      forex.start();
    }
  } 
}));
animation.setCycleCount(Animation.INDEFINITE);
animation.play();
...
public static class ForexService extends Service<ObservableList<BaseModel>> {
  private StringProperty url = new SimpleStringProperty(this, "url");
  public final void setUrl(String value) { url.set(value); }
  public final String getUrl() { return url.get(); }
  public final StringProperty urlProperty() { return url; }

  protected Task createTask() {
    final String _url = getUrl();
    return new Task<ObservableList<BaseModel>>() {
      protected String call() throws Exception {
        // getObservableList is the function which actually fetches the
        // data from the server and processes it into an ObservableList
        // which can later be fed to a TableView.
        // This function is essentially the same as the function
        // supplied in your question.
        return getObservableList(new URL(_url));
      }
    };
  }
}

The code above was just typed into StackOverflow without compilation or testing, so you might have some minor errors here or there, but I think it gives you the gist of the idea.

You might note that all that trickiness about passing the url through to the service so that it can't be modified on a different thread is just copied straight from the Service javadoc example. Also if your url is just some static thing which never changes, then you could code it as a static final constant and not bother making it a variable parameter.

Note that use of a TableView is not an issue (you could use a Grid or any other control, and you would still have an issue if you block the Java Application Thread).

If the above seems like a lot of work just to make a server call and fetch some data, then it should be noted that JavaFX is (currently) just a low level supplier of services for these types of things. There are 3rd party libraries such as DataFX which provide high level services for doing things like asynchronously fetching data from various sources, parsing it from various formats like xml and json and populating various JavaFX controls like TableViews and ListViews.

Upvotes: 3

user1157549
user1157549

Reputation: 71

You might be interested in using Highcharts, particularly this example: http://www.highcharts.com/stock/demo/dynamic-update

Upvotes: -1

Related Questions