前回は、PieChart.Dataに変換するためのユーティリティクラスを考えてみました。今回は、このユーティリティクラスを利用して、以下のようなアプリケーションを作成してみたいと思います。
TableViewで表示されているデータをPieChartで表示し、TableViewで値を変更すると、その変更がPieChartに反映されるようにしています。また、PieChartに表示するTableViewのデータを選択できるようにもしています。
まずは、TableViewに表示するデータを以下のように定義します。
class RecordModel {
@AutoTableColumn(order = 1, editable = false, cellStyleClass = {"name-table-cell"})
public StringProperty nameProperty() { return name; }
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); }
@AutoTableColumn(order = 2, prefWidth = 70, cellStyleClass = "value-table-cell")
public IntegerProperty value1Property() { return value1; }
private final IntegerProperty value1 = new SimpleIntegerProperty(this, "value1");
public final int getValue1() { return value1.get(); }
public final void setValue1(int value1) { this.value1.set(value1); }
@AutoTableColumn(order = 3, prefWidth = 70, cellStyleClass = "value-table-cell")
public IntegerProperty value2Property() { return value2; }
private final IntegerProperty value2 = new SimpleIntegerProperty(this, "value2");
public final int getValue2() { return value2.get(); }
public final void setValue2(int value2) { this.value2.set(value2); }
public RecordModel(String name, int value1, int value2) {
setName(name);
setValue1(value1);
setValue2(value2);
}
}
PieChartに表示する値として、value1とvalue2を定義しています。AutoTableColumnについては、こちらを参照してください。
次に、Viewを以下のように定義します。
<Scene xmlns:fx="http://javafx.com/fxml"
fx:id="scene"
width="720" height="480">
<stylesheets>
<URL value="@PieChartDynamicUpdateDemoSceneStyle.css"/>
</stylesheets>
<SplitPane dividerPositions="0.45">
<HBox id="recordRegion">
<TableView fx:id="recordTableView"
AutoTableColumnGeneration.enable="true"
editable="true"/>
<VBox>
<Button fx:id="addRecordButton" text="+" prefWidth="35"/>
<Button fx:id="removeRecordButton" text="-" prefWidth="35"/>
</VBox>
</HBox>
<VBox id="chartRegion">
<Label text="Select pieValue property name."/>
<HBox id="valueConditionRegion">
<fx:define>
<ToggleGroup fx:id="valuePropertyNameGroup"/>
</fx:define>
<RadioButton text="Value1" toggleGroup="$valuePropertyNameGroup"
userData="value1"/>
<RadioButton text="Value2" toggleGroup="$valuePropertyNameGroup"
userData="value2"/>
</HBox>
<PieChart fx:id="recordPieChart"/>
</VBox>
</SplitPane>
</Scene>
このViewに対するモデルを以下のように定義します。
class PieChartDynamicUpdateDemoModel {
public ObjectProperty<ObservableList<RecordModel>> recordsProperty() { return records; }
private final ObjectProperty<ObservableList<RecordModel>> records = new SimpleObjectProperty<>(this, "records");
public final ObservableList<RecordModel> getRecords() { return records.get(); }
public final void setRecords(ObservableList<RecordModel> records) { this.records.set(records); }
public ObjectProperty<TableView.TableViewSelectionModel<RecordModel>> recordSelectionModelProperty() { return recordSelectionModel; }
private final ObjectProperty<TableView.TableViewSelectionModel<RecordModel>> recordSelectionModel = new SimpleObjectProperty<>(this, "recordSelectionModel");
public final TableView.TableViewSelectionModel<RecordModel> getRecordSelectionModel() { return recordSelectionModel.get(); }
public final void setRecordSelectionModel(TableView.TableViewSelectionModel<RecordModel> recordSelectionModel) { this.recordSelectionModel.set(recordSelectionModel); }
public ObjectProperty<String> pieChartDataNameStepProperty() { return pieChartDataNameStep; }
private final ObjectProperty<String> pieChartDataNameStep = new SimpleObjectProperty<>(this, "pieChartDataNameStep", "name");
public final String getPieChartDataNameStep() { return pieChartDataNameStep.get(); }
public final void setPieChartDataNameStep(String pieChartDataNameStep) { this.pieChartDataNameStep.set(pieChartDataNameStep); }
public ObjectProperty<String> pieChartDataValueStepProperty() { return pieChartDataValueStep; }
private final ObjectProperty<String> pieChartDataValueStep = new SimpleObjectProperty<>(this, "pieChartDataValueStep", "value1");
public final String getPieChartDataValueStep() { return pieChartDataValueStep.get(); }
public final void setPieChartDataValueStep(String pieChartDataValueStep) { this.pieChartDataValueStep.set(pieChartDataValueStep); }
public ObjectProperty<Toggle> selectedPieChartDataValueStepProperty() { return selectedPieChartDataValueStep; }
private final ObjectProperty<Toggle> selectedPieChartDataValueStep = new SimpleObjectProperty<Toggle>(this, "selectedPieChartDataValueStep") {
@Override
protected void invalidated() {
if (get() != null) {
setPieChartDataValueStep(get().getUserData().toString());
}
}
};
public final Toggle getSelectedPieChartDataValueStep() { return selectedPieChartDataValueStep.get(); }
public final void setSelectedPieChartDataValueStep(Toggle selectedPieChartDataValueStep) { this.selectedPieChartDataValueStep.set(selectedPieChartDataValueStep); }
private int currentLastRecordIndex = 0;
public void loadRecords() {
setRecords(
FXCollections.<RecordModel>observableArrayList(
new RecordModel("Data 1", 10, 30),
new RecordModel("Data 2", 38, 22),
new RecordModel("Data 3", 20, 43),
new RecordModel("Data 4", 40, 11),
new RecordModel("Data 5", 30, 25)
)
);
currentLastRecordIndex = 5;
}
public void addNewRecord() {
if (getRecords() == null) { return; }
getRecords().add(new RecordModel(String.format("Data %1$s", ++currentLastRecordIndex), 0, 0));
}
public void removeSelectedRecord() {
if (getRecords() == null) { return; }
if (getRecordSelectionModel() == null) { return; }
getRecords().remove(getRecordSelectionModel().getSelectedItem());
}
}
PieChart.DataのnameプロパティにはRecordModelのnameプロパティの値を、PieChart.DataのpieValueプロパティには、ラジオボタンで選択されたRecordModelのプロパティの値をバインドできるようにしています。
また、このViewに対するコントローラーを以下のように定義します。
class PieChartDynamicUpdateDemoSceneController {
public ObjectProperty<PieChartDynamicUpdateDemoModel> modelProperty() { return model; }
private final ObjectProperty<PieChartDynamicUpdateDemoModel> model = new SimpleObjectProperty<PieChartDynamicUpdateDemoModel>(this, "model") {
private PieChartDynamicUpdateDemoModel currentModel;
@Override
protected void invalidated() {
PieChartDynamicUpdateDemoSceneController.this.unbind(currentModel);
currentModel = get();
PieChartDynamicUpdateDemoSceneController.this.bind(currentModel);
}
};
public final PieChartDynamicUpdateDemoModel getModel() { return model.get(); }
public final void setModel(PieChartDynamicUpdateDemoModel model) { this.model.set(model); }
@FXML
private Scene scene;
@FXML
private TableView<RecordModel> recordTableView;
@FXML
private Button addRecordButton;
@FXML
private Button removeRecordButton;
@FXML
private ToggleGroup valuePropertyNameGroup;
@FXML
private PieChart recordPieChart;
private final PieChartDataSource<RecordModel> recordPieChartDataSource = new PieChartDataSource<>();
public void performOn(Stage stage) {
stage.setScene(scene);
stage.setTitle("PieChart Dynamic Update Demo");
stage.sizeToScene();
stage.centerOnScreen();
stage.show();
}
public PieChartDynamicUpdateDemoSceneController with(PieChartDynamicUpdateDemoModel model) {
setModel(model);
if (model != null) { model.loadRecords(); }
return this;
}
protected void bind(PieChartDynamicUpdateDemoModel model) {
if (model == null) { return; }
recordTableView.itemsProperty().bindBidirectional(model.recordsProperty());
model.recordSelectionModelProperty().bind(recordTableView.selectionModelProperty());
model.selectedPieChartDataValueStepProperty().bind(valuePropertyNameGroup.selectedToggleProperty());
recordPieChartDataSource.namePropertyStepProperty().bind(model.pieChartDataNameStepProperty());
recordPieChartDataSource.pieValuePropertyStepProperty().bind(model.pieChartDataValueStepProperty());
recordPieChartDataSource.itemsProperty().bind(model.recordsProperty());
valuePropertyNameGroup.selectToggle(valuePropertyNameGroup.getToggles().get(0));
}
protected void unbind(PieChartDynamicUpdateDemoModel model) {
if (model == null) { return; }
recordTableView.itemsProperty().unbindBidirectional(model.recordsProperty());
model.recordSelectionModelProperty().unbind();
model.selectedPieChartDataValueStepProperty().unbind();
recordPieChartDataSource.namePropertyStepProperty().unbind();
recordPieChartDataSource.pieValuePropertyStepProperty().unbind();
recordPieChartDataSource.itemsProperty().unbind();
}
protected void addRecord() {
if (getModel() == null) { return; }
getModel().addNewRecord();
recordTableView.requestFocus();
recordTableView.getSelectionModel().selectLast();
}
protected void removeRecord() {
if (getModel() == null) { return; }
getModel().removeSelectedRecord();
recordTableView.requestFocus();
}
@FXML
protected void initialize() {
recordPieChartDataSource.setPieChart(recordPieChart);
addRecordButton.setOnAction(e -> addRecord());
removeRecordButton.setOnAction(e -> removeRecord());
}
}
前回作成したPieChartDataSourceに対して、必要なプロパティをバインドして、RecordModelのデータをPieChartに表示できるように設定しています。
最後に、Applicationクラスを以下のように定義します。
public class PieChartDynamicUpdateDemo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXController.of(new PieChartDynamicUpdateDemoSceneController())
.fromDefaultLocation()
.load()
.with(new PieChartDynamicUpdateDemoModel())
.performOn(primaryStage);
}
public static void main(String... args) {
launch(args);
}
}
次回は、XYChartに対して、同様のユーティリティクラスを考えてみたいと思います。