前回は、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に対して、同様のユーティリティクラスを考えてみたいと思います。