Saturday, March 2, 2013

JavaFX 2: Update StackedBarChart dynamically, with TableView.

In the former article "Create dynamic StackedBarChart of JavaFX 2" demonstrate how to update StackedBarChart dynamically, with TableView. BUT it's not a good example! Both the TableView and one data series of the StackedBarChart work with the same ObservableList<XYChart.Data>, only one data series of the StackedBarChart can be modified with the TableView. And hard to extend to update more data series.

Last article demonstrate how to implement "Editable multi-column TableView". It's modified to work with StackedBarChart, all the data series in StackedBarChart can be changed with the TableView.


Instead of share a common ObservableList<XYChart.Data>, a class MyList is implemented. It hold a ObservableList<Record> for TableView, and two ObservableList<XYChart.Data> for StackedBarChart, and also keep their content consistency.

Once editing of the table committed, the handler update the ObservableList<Record> for TableView, and also the corresponding ObservableList<XYChart.Data> for StackedBarChart.

package javafxmulticolumnchart;

import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

/**
*
* @web http://java-buddy.blogspot.com/
*/
public class JavaFXMultiColumnChart extends Application {

public class Record{
private SimpleStringProperty fieldMonth;
private SimpleDoubleProperty fieldValue1;
private SimpleDoubleProperty fieldValue2;

Record(String fMonth, double fValue1, double fValue2){
this.fieldMonth = new SimpleStringProperty(fMonth);
this.fieldValue1 = new SimpleDoubleProperty(fValue1);
this.fieldValue2 = new SimpleDoubleProperty(fValue2);
}

public String getFieldMonth() {
return fieldMonth.get();
}

public double getFieldValue1() {
return fieldValue1.get();
}

public double getFieldValue2() {
return fieldValue2.get();
}

public void setFieldMonth(String fMonth) {
fieldMonth.set(fMonth);
}

public void setFieldValue1(Double fValue1) {
fieldValue1.set(fValue1);
}

public void setFieldValue2(Double fValue2) {
fieldValue2.set(fValue2);
}
}

class MyList {

ObservableList<Record> dataList;
ObservableList<XYChart.Data> xyList1;
ObservableList<XYChart.Data> xyList2;

MyList(){
dataList = FXCollections.observableArrayList();
xyList1 = FXCollections.observableArrayList();
xyList2 = FXCollections.observableArrayList();
}

public void add(Record r){
dataList.add(r);
xyList1.add(new XYChart.Data(r.getFieldMonth(), r.getFieldValue1()));
xyList2.add(new XYChart.Data(r.getFieldMonth(), r.getFieldValue2()));
}

public void update1(int pos, Double val){
xyList1.set(pos, new XYChart.Data(xyList1.get(pos).getXValue(), val));
}

public void update2(int pos, Double val){
xyList2.set(pos, new XYChart.Data(xyList2.get(pos).getXValue(), val));
}
}

MyList myList;

private TableView<Record> tableView = new TableView<>();

public static void main(String[] args) {
launch(args);
}

@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("java-buddy.blogspot.com");

//prepare myList
myList = new MyList();
myList.add(new Record("January", 100, 120));
myList.add(new Record("February", 200, 210));
myList.add(new Record("March", 50, 70));
myList.add(new Record("April", 75, 50));
myList.add(new Record("May", 110, 120));
myList.add(new Record("June", 300, 200));
myList.add(new Record("July", 111, 100));
myList.add(new Record("August", 30, 50));
myList.add(new Record("September", 75, 70));
myList.add(new Record("October", 55, 50));
myList.add(new Record("November", 225, 225));
myList.add(new Record("December", 99, 100));

Group root = new Group();

tableView.setEditable(true);

Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {

@Override
public TableCell call(TableColumn p) {
return new EditingCell();
}
};

TableColumn columnMonth = new TableColumn("Month");
columnMonth.setCellValueFactory(
new PropertyValueFactory<Record,String>("fieldMonth"));
columnMonth.setMinWidth(60);

TableColumn columnValue1 = new TableColumn("Value 1");
columnValue1.setCellValueFactory(
new PropertyValueFactory<Record,Double>("fieldValue1"));
columnValue1.setMinWidth(60);

TableColumn columnValue2 = new TableColumn("Value 2");
columnValue2.setCellValueFactory(
new PropertyValueFactory<Record,Double>("fieldValue2"));
columnValue2.setMinWidth(60);

//--- Add for Editable Cell of Value field, in Double
columnValue1.setCellFactory(cellFactory);
columnValue1.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Record, Double>>() {

@Override public void handle(TableColumn.CellEditEvent<Record, Double> t) {
((Record)t.getTableView().getItems().get(
t.getTablePosition().getRow())).setFieldValue1(t.getNewValue());

int pos = t.getTablePosition().getRow();
myList.update1(pos, t.getNewValue());
}
});

columnValue2.setCellFactory(cellFactory);
columnValue2.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Record, Double>>() {

@Override public void handle(TableColumn.CellEditEvent<Record, Double> t) {
((Record)t.getTableView()
.getItems()
.get(t.getTablePosition().getRow())).setFieldValue2(t.getNewValue());

int pos = t.getTablePosition().getRow();
myList.update2(pos, t.getNewValue());
}
});

//--- Prepare StackedBarChart

List<String> monthLabels = Arrays.asList(
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December");

final CategoryAxis xAxis1 = new CategoryAxis();
final NumberAxis yAxis1 = new NumberAxis();
xAxis1.setLabel("Month");
xAxis1.setCategories(FXCollections.<String> observableArrayList(monthLabels));
yAxis1.setLabel("Value 1");
XYChart.Series XYSeries1 = new XYChart.Series(myList.xyList1);
XYSeries1.setName("XYChart.Series 1");

final CategoryAxis xAxis2 = new CategoryAxis();
final NumberAxis yAxis2 = new NumberAxis();
xAxis2.setLabel("Month");
xAxis2.setCategories(FXCollections.<String> observableArrayList(monthLabels));
yAxis2.setLabel("Value 2");
XYChart.Series XYSeries2 = new XYChart.Series(myList.xyList2);
XYSeries2.setName("XYChart.Series 2");

final StackedBarChart<String,Number> stackedBarChart = new StackedBarChart<>(xAxis1,yAxis1);
stackedBarChart.setTitle("StackedBarChart");
stackedBarChart.getData().addAll(XYSeries1, XYSeries2);
//---
tableView.setItems(myList.dataList);
tableView.getColumns().addAll(columnMonth, columnValue1, columnValue2);

HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.getChildren().addAll(tableView, stackedBarChart);

root.getChildren().add(hBox);

primaryStage.setScene(new Scene(root, 750, 400));
primaryStage.show();
}

class EditingCell extends TableCell<Record, Double> {
private TextField textField;

public EditingCell() {}

@Override
public void startEdit() {
super.startEdit();

if (textField == null) {
createTextField();
}

setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
}

@Override
public void cancelEdit() {
super.cancelEdit();

setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}

@Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);

if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}

setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}

private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()*2);
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {

@Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(Double.parseDouble(textField.getText()));
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}

private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}

}


Another example to make PieChart and LineChart update dynamically with TableView.


No comments:

Post a Comment