JavaFX のラインチャートを使っていると、チャート内に縦線あるいは横線の固定線を描画したい場合があります。例えば、目標値を強調する場合や、管理範囲を表示したい場合などです。参考サイト [1] にぴったりの例がありましたので紹介します。
動作環境は次の通りです。
- OS: Fedora 23 x86_64
- jdk1.8.0_74-1.8.0_74-fcs.x86_64 (Oracle)
- IDE: NetBeans IDE 8.1
まず、LineChartWithMarkers.java ですが、これは参考サイト [1] で紹介されている LineChart を継承した内部クラス LineChartWithMarkers を、通常のクラスにして、少しだけ書き換えたものです。
リスト:LineChartWithMarkers.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | package linechartsample; import java.util.Objects; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.chart.Axis; import javafx.scene.chart.LineChart; import javafx.scene.shape.Line; public class LineChartWithMarkers<X, Y> extends LineChart<X, Y> { private final ObservableList<Data<X, Y>> horizontalMarkers; private final ObservableList<Data<X, Y>> verticalMarkers; public LineChartWithMarkers(Axis<X> xAxis, Axis<Y> yAxis) { super (xAxis, yAxis); horizontalMarkers = FXCollections.observableArrayList(data -> new Observable[]{data.YValueProperty()}); horizontalMarkers.addListener((InvalidationListener) observable -> layoutPlotChildren()); verticalMarkers = FXCollections.observableArrayList(data -> new Observable[]{data.XValueProperty()}); verticalMarkers.addListener((InvalidationListener) observable -> layoutPlotChildren()); } public void addHorizontalValueMarker(Data<X, Y> marker) { Objects.requireNonNull(marker, "the marker must not be null" ); if (horizontalMarkers.contains(marker)) { return ; } Line line = new Line(); line.setStyle( "-fx-stroke:green;-fx-stroke-width:1px;" ); marker.setNode(line); getPlotChildren().add(line); horizontalMarkers.add(marker); } public void removeHorizontalValueMarker(Data<X, Y> marker) { Objects.requireNonNull(marker, "the marker must not be null" ); if (marker.getNode() != null ) { getPlotChildren().remove(marker.getNode()); marker.setNode( null ); } horizontalMarkers.remove(marker); } public void addVerticalValueMarker(Data<X, Y> marker) { Objects.requireNonNull(marker, "the marker must not be null" ); if (verticalMarkers.contains(marker)) { return ; } Line line = new Line(); line.setStyle( "-fx-stroke:orange;-fx-stroke-width:1px;" ); marker.setNode(line); getPlotChildren().add(line); verticalMarkers.add(marker); } public void removeVerticalValueMarker(Data<X, Y> marker) { Objects.requireNonNull(marker, "the marker must not be null" ); if (marker.getNode() != null ) { getPlotChildren().remove(marker.getNode()); marker.setNode( null ); } verticalMarkers.remove(marker); } @Override protected void layoutPlotChildren() { horizontalMarkers.stream().forEach((horizontalMarker) -> { drawHorizontalMarker(horizontalMarker); }); verticalMarkers.stream().forEach((verticalMarker) -> { drawVerticalMarker(verticalMarker); }); super .layoutPlotChildren(); } private void drawHorizontalMarker(Data<X, Y> horizontalMarker) { Line line = (Line) horizontalMarker.getNode(); line.setStartX( 0 ); line.setEndX(getBoundsInLocal().getWidth()); line.setStartY(getYAxis().getDisplayPosition(horizontalMarker.getYValue()) + 0.5 ); // 0.5 for crispness line.setEndY(line.getStartY()); line.toFront(); } private void drawVerticalMarker(Data<X, Y> verticalMarker) { Line line = (Line) verticalMarker.getNode(); line.setStartX(getXAxis().getDisplayPosition(verticalMarker.getXValue()) + 0.5 ); // 0.5 for crispness line.setEndX(line.getStartX()); line.setStartY(0d); line.setEndY(getBoundsInLocal().getHeight()); line.toFront(); } } |
次に、サンプルチャートを描画する LineChartSample.java ですが、これは過去の記事 [2] をベースに、横線を前述の LineChartWithMarkers クラスの addHorizontalValueMarker メソッドで描画する機能を追加しています。なお、縦線を描画するには addVertialValueMarker メソッドを用います。
リスト:LineChartSample.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | package linechartsample; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart.Series; import javafx.stage.Stage; public class LineChartSample extends Application { @Override public void start(Stage stage) { stage.setTitle( "Line Chart Sample" ); //defining the axes final CategoryAxis xAxis = new CategoryAxis(); final NumberAxis yAxis = new NumberAxis(); xAxis.setLabel( "X軸ラベル(年)" ); yAxis.setLabel( "Y軸ラベル(数値)" ); //creating the chart LineChartWithMarkers<String, Number> lineChart = new LineChartWithMarkers<>(xAxis, yAxis); lineChart.setData(getChartData()); lineChart.setTitle( "折れ線グラフのサンプル" ); lineChart.addHorizontalValueMarker( new XYChart.Data<>( "" , 1 )); lineChart.addHorizontalValueMarker( new XYChart.Data<>( "" , - 1 )); Scene scene = new Scene(lineChart, 600 , 400 ); scene.getStylesheets().add(getClass().getResource( "LineChart.css" ).toExternalForm()); stage.setScene(scene); stage.show(); } /** * * @return ObservableList<XYChart.Series<String, Double>> */ private ObservableList<XYChart.Series<String, Number>> getChartData() { double y1 = 0.5 ; double y2 = - 0.5 ; Series<String, Number> series1 = new Series<>(); Series<String, Number> series2 = new Series<>(); series1.setName( "系列1" ); series2.setName( "系列2" ); for ( int i = 2011 ; i < 2021 ; i++) { series1.getData().add( new XYChart.Data<>(Integer.toString(i), y1)); y1 = y1 + Math.random() - . 5 ; series2.getData().add( new XYChart.Data<>(Integer.toString(i), y2)); y2 = y2 + Math.random() - . 5 ; } ObservableList<XYChart.Series<String, Number>> seriesList = FXCollections.observableArrayList(); seriesList.add(series1); seriesList.add(series2); return seriesList; } public static void main(String[] args) { launch(args); } } |
LineChart.css は過去の記事 [2] と同じです。
リスト:LineChart.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | .chart { -fx- padding : 10px ; -fx- background-color : white ; } .chart-content { -fx- padding : 10px ; } .chart-title { -fx- font-size : 18pt ; } .chart-legend { -fx- font-size : 12pt ; -fx- background-color : transparent ; -fx- padding : 10px ; } .axis-label { -fx- font-size : 14pt ; } .axis { -fx-tick-label- font : 12pt system; } .chart-series-line { -fx-stroke- width : 2px ; -fx-effect: null; } .default-color 0 .chart-series-line { -fx-stroke: red ; } .default-color 1 .chart-series-line { -fx-stroke: blue ; } .default-color 0 .chart-line-symbol { -fx- background-color : red , #f88 ; -fx-background-radius: 6px ; -fx- padding : 6px ; } .default-color 1 .chart-line-symbol { -fx- background-color : blue , #88f ; -fx-background-radius: 0 ; -fx- padding : 6px ; } |
実行例を以下に示します。データは乱数でプロットされていますので、場合によっては横線 y = ± 1 が表示エリアに含まれない場合があります。
参考サイト
- java - How to add two vertical lines with JavaFX LineChart - Stack Overflow
- bitWalk's: JavaFX: LineChart を使いこなそう (2)
0 件のコメント:
コメントを投稿