Reputation: 110
I'm just a nooby coder but I've been experimenting with JavaFX and been playing around with demos making my own applications aswell. Not sure of the best way to approach this but I'm making an automatic stacked barchart maker using JavaFX which relies on data in a format like this:
Client
- Title - Value
- Title - Value
- Title - Value
Client
- Title - Value
- Title - Value
- Title - Value
Clients are able to be added and when I add a new title it has to apply for all clients
http://i1101.photobucket.com/albums/g435/izzypod5/chart_zps7da8c9df.png
not enough rep to post img
I have been looking at ways to do it such as:
Some sort of maps of data being added to client after client name
public void setClientData(ObservableList<Client> clients) {
ArrayList<XYChart.Series> seriesList = new ArrayList<XYChart.Series>();
for (Client client : clients){
XYChart.Series<String, Integer> series = new XYChart.Series();
series.setName(client.getName());
seriesList.add(series);
}
for(Series series :seriesList){
for( int i = 0; i <= twc.length - 1; i++){
series.getData().add(new XYChart.Data<String, Number>(twc[i], 1000));
}
barChart.getData().add(series);
}
So as you see I'm getting client values and making the series iterate through a loop and setting the name for each I will be putting the titles where the twc string array is and the values corresponding the default 1000 value.
You can assume I have a list/table of clients and titles which can be added dynamically but titles are fixed for each client however The values of the titles change for each client is what I'm trying to get at.
Any ideas on how what is the best way to approach this problem would be greatly appreciated :)
Upvotes: 0
Views: 318
Reputation: 209684
I would implement Client
something like this:
import java.util.HashMap;
import java.util.Map;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Client {
private final StringProperty name = new SimpleStringProperty(this, "name");
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name ;
}
private final Map<Title, DoubleProperty> values = new HashMap<>();
public Client(String name) {
setName(name);
}
public DoubleProperty valueProperty(Title title) {
// In Java 8, just do
// return values.computeIfAbsent(title, t -> new SimpleDoubleProperty());
DoubleProperty value = values.get(title);
if (value == null) {
value = new SimpleDoubleProperty();
values.put(title, value);
}
return value ;
}
public final double getValue(Title title) {
return valueProperty(title).get();
}
public final void setValue(Title title, double value) {
valueProperty(title).set(value);
}
public Map<Title, DoubleProperty> getValues() {
return values ;
}
}
Title
is likely pretty trivial, you may even be able to use a StringProperty
.
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Title {
private final StringProperty name = new SimpleStringProperty(this, "name");
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name ;
}
public Title(String name) {
setName(name);
}
}
You could now just create observable lists of Clients and Titles, and register listeners with them to update the data for a StackedBarChart
when items are added or removed from the lists. A more concise way, if you are using Java 8, is to use the EasyBind framework and just bind the contents of the series and data to maps of the data in the lists.
This example uses EasyBind:
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.fxmisc.easybind.EasyBind;
public class StackedBarChartExample extends Application {
private final ObservableList<Title> titles = FXCollections.observableArrayList();
private final ObservableList<Client> clients = FXCollections.observableArrayList();
private final ObservableList<Series<String, Number>> mappedSeries = EasyBind.map(
clients, client -> {
Series<String, Number> series = new Series<>();
ObservableList<Data<String, Number>> mappedData = EasyBind.map(
titles,
title -> {
Data<String, Number> data = new Data<>();
data.XValueProperty().bind(title.nameProperty());
data.YValueProperty().bind(client.valueProperty(title));
return data;
});
series.setData(mappedData);
series.nameProperty().bind(client.nameProperty());
return series;
});
@Override
public void start(Stage primaryStage) {
NumberAxis yAxis = new NumberAxis();
CategoryAxis xAxis = new CategoryAxis();
xAxis.setAutoRanging(false);
StackedBarChart<String, Number> barChart = new StackedBarChart<>(xAxis,
yAxis);
barChart.setAnimated(false);
Bindings.bindContent(barChart.getData(), mappedSeries);
xAxis.setCategories(EasyBind.map(titles, Title::getName));
Title awaitingResponse = new Title("Awaiting Response");
Title agreed = new Title("Agreed");
Title workInProgress = new Title("Work in progress");
titles.addAll(awaitingResponse, agreed, workInProgress);
for (int i = 1; i <= 5; i++) {
Client client = new Client("Test " + i);
client.setValue(awaitingResponse, i * 1000);
client.setValue(agreed, 3000);
client.setValue(workInProgress, (6 - i) * 1000);
clients.add(client);
}
VBox controls = new VBox(5);
final Random rng = new Random();
Button newClientButton = new Button("Add client");
newClientButton.setOnAction(event -> {
Client client = new Client("Test " + (clients.size() + 1));
for (Title title : titles) {
client.setValue(title, rng.nextInt(5000));
}
clients.add(client);
});
HBox titleControls = new HBox(5);
titleControls.setAlignment(Pos.CENTER);
TextField newTitleText = new TextField();
Button newTitleButton = new Button("Add title");
newTitleButton.setOnAction(event -> {
Title title = new Title(newTitleText.getText());
for (Client client : clients) {
client.setValue(title, rng.nextInt(5000));
}
titles.add(title);
});
titleControls.getChildren().addAll(newTitleText, newTitleButton);
controls.setPadding(new Insets(5));
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(titleControls, newClientButton);
BorderPane root = new BorderPane();
root.setCenter(barChart);
root.setBottom(controls);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 0