Extjfx
JavaFX extensions
Install / Use
/learn @extjfx/ExtjfxREADME
ExtJFX
ExtJFX is a small library developed at CERN containing features needed by our JavaFX applications that are not supported by the standard JavaFX toolkit. The library consists of 3 modules:
- extjfx-chart: zooming, panning, data annotations, value/range indicators, chart decorations, overlaying different types of charts, etc.
- extjfx-fxml:
FxmlViewclass that simplifies loading FXML files using conventional names - extjfx-test:
FxJUnit4Runnerto execute GUI tests - extjfx-samples: Executable jar with chart samples
Build Artifacts
extjfx-chart
XYChartPane
The central class of the cern.extjfx.chart package is XYChartPane. It is a container that can hold one or more instances of XYChart (e.g. LineChart, AreaChart, BarChart). XYChartPane brings support for overlaying different chart types on top of each other (as on the figure below) and possibility to add chart plugins (instances of XYChartPlugin) which can be either interacting components e.g. Zoomer or Panner, or passive graphical elements drawn on the chart such as labels or data indicators.

public class MixedChartSample extends Application {
private static final List<String> DAYS = new ArrayList<>(
Arrays.asList("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"));
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("Mixed Chart Sample");
BarChart<String, Number> barChart = new BarChart<>(createXAxis(), createYAxis());
barChart.getStyleClass().add("chart1");
barChart.setAnimated(false);
barChart.getYAxis().setLabel("Data 1");
barChart.getYAxis().setSide(Side.LEFT);
barChart.getData().add(new Series<>("Data 1", createTestData(3)));
LineChart<String, Number> lineChart = new LineChart<>(createXAxis(), createYAxis());
lineChart.getStyleClass().add("chart2");
lineChart.setAnimated(false);
lineChart.setCreateSymbols(true);
lineChart.getYAxis().setLabel("Data 2");
lineChart.getYAxis().setSide(Side.RIGHT);
lineChart.getData().add(new Series<>("Data 2", createTestData(10)));
ScatterChart<String, Number> scatterChart = new ScatterChart<>(createXAxis(), createYAxis());
scatterChart.getStyleClass().add("chart3");
scatterChart.setAnimated(false);
scatterChart.getYAxis().setLabel("Data 3");
scatterChart.getYAxis().setSide(Side.RIGHT);
scatterChart.getData().add(new Series<>("Data 3", createTestData(20)));
XYChartPane<String, Number> chartPane = new XYChartPane<>(barChart);
chartPane.setTitle("Mixed chart types");
chartPane.setCommonYAxis(false);
chartPane.getOverlayCharts().addAll(lineChart, scatterChart);
chartPane.getPlugins().addAll(new CrosshairIndicator<>(), new DataPointTooltip<>());
chartPane.getStylesheets().add("mixed-chart-sample.css");
BorderPane borderPane = new BorderPane(chartPane);
Scene scene = new Scene(borderPane, 800, 600);
stage.setScene(scene);
stage.show();
}
private NumericAxis createYAxis() {
NumericAxis yAxis = new NumericAxis();
yAxis.setAnimated(false);
yAxis.setForceZeroInRange(false);
yAxis.setAutoRangePadding(0.1);
yAxis.setAutoRangeRounding(true);
return yAxis;
}
private CategoryAxis createXAxis() {
CategoryAxis xAxis = new CategoryAxis();
xAxis.setAnimated(false);
return xAxis;
}
private ObservableList<Data<String, Number>> createTestData(double refVal) {
Random rnd = new Random();
List<Data<String, Number>> data = new ArrayList<>();
for (int i = 0; i < DAYS.size(); i++) {
data.add(new Data<>(DAYS.get(i), refVal - Math.abs(3 - i) + rnd.nextDouble()));
}
return FXCollections.observableArrayList(data);
}
public static void main(String[] args) {
launch(args);
}
}
</details>
<details><summary>The corresponding CSS (expand)</summary>
.chart1 .chart-bar { -fx-bar-fill: #22bad9; }
.chart1 .axis:left { -fx-tick-label-fill: #22bad9; }
.chart1 .axis:left .axis-label { -fx-text-fill: #22bad9; }
.chart2 .axis:right { -fx-tick-label-fill: #c62b00; }
.chart2 .axis:right .axis-label { -fx-text-fill: #c62b00; }
.chart2 .chart-series-line { -fx-stroke: #c62b00; }
.chart2 .chart-line-symbol { -fx-background-color: #c62b00, white; }
.chart3 .axis:right { -fx-tick-label-fill: green; }
.chart3 .axis:right .axis-label { -fx-text-fill: green; }
.chart3 .chart-symbol {
-fx-background-color: green;
-fx-background-radius: 0;
-fx-padding: 7px 5px 7px 5px;
-fx-shape: "M5,0 L10,9 L5,18 L0,9 Z";
}
</details>
The XYChartPane allows having a single (shared) Y axis or distinct axes, one per overlaid chart.
Note that in order to draw charts properly on top of each other some properties of the overlaid charts are overridden - see JavaDoc of XYChartPane for details.
Chart Plugins
Chart plugins are add-ons to the standard charts that can be added to the XYChartPane to either interact with chart content or to decorate it. Currently the package provides the following plugins:
- ChartOverlay allows adding any node on top of the chart area.
- CrosshairIndicator a cross (horizontal and vertical line) following mouse cursor and displaying current coordinates
- DataPointTooltip a tooltip label displaying coordinates of the data point hovered by the mouse cursor
- Zoomer zooms the plot area to the dragged rectangle
- Panner allows dragging the visible data window with mouse cursor
- XValueIndicator and YValueIndicator a vertical or horizontal line (accordingly) indicating specified X or Y value, with optional text label that can be used to describe the indicated value
- XRangeIndicator and YRangeIndicator a rectangle indicating vertical or horizontal range (accordingly) of X or Y values, with optional text label that can be used to describe the indicated range
The following example presents all plugins on a single chart pane.
<details><summary>Source code (expand)</summary>public class PluginsSample extends Application {
@Override
public void start(Stage stage) {
stage.setTitle("Plugins Sample");
NumericAxis xAxis = new NumericAxis();
xAxis.setLabel("X Values");
NumericAxis yAxis = new NumericAxis();
yAxis.setAutoRangePadding(0.1);
yAxis.setLabel("Y Values");
LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis);
chart.getData().add(new Series<>("Test Data", createTestData()));
XYChartPane<Number, Number> chartPane = new XYChartPane<>(chart);
XValueIndicator<Number> internalStop = new XValueIndicator<>(75, "Internal Stop");
internalStop.setLabelPosition(0.95);
chartPane.getPlugins().add(internalStop);
YValueIndicator<Number> yMin = new YValueIndicator<>(-7.5, "MIN");
yMin.setLabelPosition(0.1);
YValueIndicator<Number> yMax = new YValueIndicator<>(7.5, "MAX");
yMax.setLabelPosition(0.1);
chartPane.getPlugins().addAll(yMin, yMax);
XRangeIndicator<Number> xRange = new XRangeIndicator<>(40, 60, "X Range");
xRange.setLabelVerticalPosition(0.95);
chartPane.getPlugins().add(xRange);
YRangeIndicator<Number> thresholds = new YRangeIndicator<>(-5, 5, "Thresholds");
thresholds.setLabelHorizontalAnchor(HPos.RIGHT);
thresholds.setLabelHorizontalPosition(0.95);
thresholds.setLabelVerticalAnchor(VPos.TOP);
thresholds.setLabelVerticalPosition(0.95);
chartPane.getPlugins().add(thresholds);
Related Skills
node-connect
345.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
104.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
345.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
