Гибкая настройка графиков в JavaFX

в 13:34, , рубрики: css, java, javafx, велосипедостроение, графики, Программирование

Гибкая настройка графиков в JavaFX

JavaFX довольно странная штука. С одной стороны он кажется безумно продуманным и удобным настолько, что хочется использовать его где только можно. А с другой стороны он настолько топорный, что хочется его переписать. Некоторые части графического интерфейса работают совсем странно, другие объявлены как final и дополнить их невозможно.
Если вам интересно, как представлены графические элементы в JavaFX и как можно разнообразить свой график, то открывайте кат, на свой страх и риск.

Структура графического интерфейса в JavaFX

JavaFX — это RIA фреймворк. Так же, все настройки вида графических элементов можно изменить только через СSS, что делать невозможным обычные методы изменение интерфейса, которые использовались в Swing или AWT через Graphics.
Так же, в отличии от других Java GUI фреймворков, создавать новые графические элементы в JavaFX крайне сложно из-за сложной реализации самого JavaFX. Поэтому, во время создания своих элементов их конструируют из старых.

Так поступают и сами разработчики JavaFX во время создания сложных контроллов, таких как графики.
В этом нет ничего нового, но главное отличие JavaFX от остальных фрейворков состоит в том, что структура являет собой дерево с полным доступом, то есть можно достучатся до всех этих элементов и легко настроить их (через CSS или еще как-то).

В качестве примера, я приведу настройку LineChart.

Настройка LineChart

Что бы что-то настроить, нужно четко понимать, из чего оно стоит.
Понять, из чего же состоит LineChart нам поможет функция lookupAll и наконец-то адекватный перевод компонентов JavaFX в строку.

Выполнив такой код:

VBox vBox = new VBox();
LineChart chart = new LineChart(new NumberAxis(), new NumberAxis());
chart.getData().add(new XYChart.Series<>("Temp1", FXCollections.observableArrayList(new XYChart.Data<Object, Object>(1, 1), new XYChart.Data<Object, Object>(2, 2), new XYChart.Data<Object, Object>(3, 4))));
vBox.getChildren().add(chart);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
((XYChart.Series) chart.getData().get(0)).getData().addAll(new XYChart.Data<>(5, 5));
chart.lookupAll("*").forEach(c->{
    System.out.println(c);
    System.out.println();
});

Результат
LineChart@be5a370[styleClass=chart]

Label@7415bead[styleClass=label chart-title]''

Text[text="", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL_VERTICAL_CENTER, font=Font[name=System Regular, family=System, style=Regular, size=18.200000762939453], fontSmoothingType=LCD, fill=0x333333ff]

Chart$1@2fca6f84[styleClass=chart-content]

Region@285a8729[styleClass=chart-plot-background]

XYChart$1@53306ff

Path[elements=[], fill=null, fillRule=NON_ZERO]

Path[elements=[MoveTo[x=39.0, y=45.0], LineTo[x=481.0, y=45.0], LineTo[x=481.0, y=81.0], LineTo[x=39.0, y=81.0], ClosePath, MoveTo[x=39.0, y=116.0], LineTo[x=481.0, y=116.0], LineTo[x=481.0, y=152.0], LineTo[x=39.0, y=152.0], ClosePath, MoveTo[x=39.0, y=187.0], LineTo[x=481.0, y=187.0], LineTo[x=481.0, y=223.0], LineTo[x=39.0, y=223.0], ClosePath, MoveTo[x=39.0, y=258.0], LineTo[x=481.0, y=258.0], LineTo[x=481.0, y=294.0], LineTo[x=39.0, y=294.0], ClosePath], fill=null, fillRule=NON_ZERO]

Path[elements=[MoveTo[x=73.5, y=10.0], LineTo[x=73.5, y=329.0], MoveTo[x=107.5, y=10.0], LineTo[x=107.5, y=329.0], MoveTo[x=141.5, y=10.0], LineTo[x=141.5, y=329.0], MoveTo[x=175.5, y=10.0], LineTo[x=175.5, y=329.0], MoveTo[x=209.5, y=10.0], LineTo[x=209.5, y=329.0], MoveTo[x=243.5, y=10.0], LineTo[x=243.5, y=329.0], MoveTo[x=277.5, y=10.0], LineTo[x=277.5, y=329.0], MoveTo[x=311.5, y=10.0], LineTo[x=311.5, y=329.0], MoveTo[x=345.5, y=10.0], LineTo[x=345.5, y=329.0], MoveTo[x=379.5, y=10.0], LineTo[x=379.5, y=329.0], MoveTo[x=413.5, y=10.0], LineTo[x=413.5, y=329.0], MoveTo[x=447.5, y=10.0], LineTo[x=447.5, y=329.0], MoveTo[x=481.5, y=10.0], LineTo[x=481.5, y=329.0]], fill=null, fillRule=NON_ZERO, stroke=0xdcdcdcff, strokeWidth=1.0]

Path[elements=[MoveTo[x=39.0, y=294.5], LineTo[x=481.0, y=294.5], MoveTo[x=39.0, y=258.5], LineTo[x=481.0, y=258.5], MoveTo[x=39.0, y=223.5], LineTo[x=481.0, y=223.5], MoveTo[x=39.0, y=187.5], LineTo[x=481.0, y=187.5], MoveTo[x=39.0, y=152.5], LineTo[x=481.0, y=152.5], MoveTo[x=39.0, y=116.5], LineTo[x=481.0, y=116.5], MoveTo[x=39.0, y=81.5], LineTo[x=481.0, y=81.5], MoveTo[x=39.0, y=45.5], LineTo[x=481.0, y=45.5], MoveTo[x=39.0, y=10.5], LineTo[x=481.0, y=10.5]], fill=null, fillRule=NON_ZERO, stroke=0xdcdcdcff, strokeWidth=1.0]

Line[startX=39.5, startY=10.0, endX=39.5, endY=329.0, stroke=0x646464ff, strokeWidth=1.0]

Line[startX=39.0, startY=329.5, endX=481.0, endY=329.5, stroke=0x646464ff, strokeWidth=1.0]

Group@2bdd794e[styleClass=plot-content]

Path[elements=[MoveTo[x=136.0, y=248.0], LineTo[x=136.0, y=248.0], LineTo[x=272.0, y=177.0], LineTo[x=408.0, y=35.0]], fill=null, fillRule=NON_ZERO, stroke=0xf3622dff, strokeWidth=3.0]

StackPane@26193c9c[styleClass=chart-line-symbol series0 data0 default-color0]

StackPane@6bb5f434[styleClass=chart-line-symbol series0 data1 default-color0]

StackPane@38bfa9d7[styleClass=chart-line-symbol series0 data2 default-color0]

NumberAxis@392fac3b[styleClass=axis]

Label@2051bac6[styleClass=label axis-label]''

Text[text="", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL_VERTICAL_CENTER, font=Font[name=System Regular, family=System, style=Regular, size=13.0], fontSmoothingType=LCD, fill=0x333333ff]

Path[elements=[MoveTo[x=0.0, y=0.0], LineTo[x=0.0, y=8.0], MoveTo[x=34.0, y=0.0], LineTo[x=34.0, y=8.0], MoveTo[x=68.0, y=0.0], LineTo[x=68.0, y=8.0], MoveTo[x=102.0, y=0.0], LineTo[x=102.0, y=8.0], MoveTo[x=136.0, y=0.0], LineTo[x=136.0, y=8.0], MoveTo[x=170.0, y=0.0], LineTo[x=170.0, y=8.0], MoveTo[x=204.0, y=0.0], LineTo[x=204.0, y=8.0], MoveTo[x=238.0, y=0.0], LineTo[x=238.0, y=8.0], MoveTo[x=272.0, y=0.0], LineTo[x=272.0, y=8.0], MoveTo[x=306.0, y=0.0], LineTo[x=306.0, y=8.0], MoveTo[x=340.0, y=0.0], LineTo[x=340.0, y=8.0], MoveTo[x=374.0, y=0.0], LineTo[x=374.0, y=8.0], MoveTo[x=408.0, y=0.0], LineTo[x=408.0, y=8.0], MoveTo[x=442.0, y=0.0], LineTo[x=442.0, y=8.0]], fill=null, fillRule=NON_ZERO, stroke=0xc3c3c3ff, strokeWidth=1.0]

Path[elements=[MoveTo[x=7.0, y=1.0], LineTo[x=7.0, y=5.0], MoveTo[x=14.0, y=1.0], LineTo[x=14.0, y=5.0], MoveTo[x=20.0, y=1.0], LineTo[x=20.0, y=5.0], MoveTo[x=27.0, y=1.0], LineTo[x=27.0, y=5.0], MoveTo[x=41.0, y=1.0], LineTo[x=41.0, y=5.0], MoveTo[x=48.0, y=1.0], LineTo[x=48.0, y=5.0], MoveTo[x=54.0, y=1.0], LineTo[x=54.0, y=5.0], MoveTo[x=61.0, y=1.0], LineTo[x=61.0, y=5.0], MoveTo[x=68.0, y=1.0], LineTo[x=68.0, y=5.0], MoveTo[x=75.0, y=1.0], LineTo[x=75.0, y=5.0], MoveTo[x=82.0, y=1.0], LineTo[x=82.0, y=5.0], MoveTo[x=88.0, y=1.0], LineTo[x=88.0, y=5.0], MoveTo[x=95.0, y=1.0], LineTo[x=95.0, y=5.0], MoveTo[x=109.0, y=1.0], LineTo[x=109.0, y=5.0], MoveTo[x=116.0, y=1.0], LineTo[x=116.0, y=5.0], MoveTo[x=122.0, y=1.0], LineTo[x=122.0, y=5.0], MoveTo[x=129.0, y=1.0], LineTo[x=129.0, y=5.0], MoveTo[x=143.0, y=1.0], LineTo[x=143.0, y=5.0], MoveTo[x=150.0, y=1.0], LineTo[x=150.0, y=5.0], MoveTo[x=156.0, y=1.0], LineTo[x=156.0, y=5.0], MoveTo[x=163.0, y=1.0], LineTo[x=163.0, y=5.0], MoveTo[x=177.0, y=1.0], LineTo[x=177.0, y=5.0], MoveTo[x=184.0, y=1.0], LineTo[x=184.0, y=5.0], MoveTo[x=190.0, y=1.0], LineTo[x=190.0, y=5.0], MoveTo[x=197.0, y=1.0], LineTo[x=197.0, y=5.0], MoveTo[x=211.0, y=1.0], LineTo[x=211.0, y=5.0], MoveTo[x=218.0, y=1.0], LineTo[x=218.0, y=5.0], MoveTo[x=224.0, y=1.0], LineTo[x=224.0, y=5.0], MoveTo[x=231.0, y=1.0], LineTo[x=231.0, y=5.0], MoveTo[x=245.0, y=1.0], LineTo[x=245.0, y=5.0], MoveTo[x=252.0, y=1.0], LineTo[x=252.0, y=5.0], MoveTo[x=258.0, y=1.0], LineTo[x=258.0, y=5.0], MoveTo[x=265.0, y=1.0], LineTo[x=265.0, y=5.0], MoveTo[x=279.0, y=1.0], LineTo[x=279.0, y=5.0], MoveTo[x=286.0, y=1.0], LineTo[x=286.0, y=5.0], MoveTo[x=292.0, y=1.0], LineTo[x=292.0, y=5.0], MoveTo[x=299.0, y=1.0], LineTo[x=299.0, y=5.0], MoveTo[x=306.0, y=1.0], LineTo[x=306.0, y=5.0], MoveTo[x=313.0, y=1.0], LineTo[x=313.0, y=5.0], MoveTo[x=320.0, y=1.0], LineTo[x=320.0, y=5.0], MoveTo[x=326.0, y=1.0], LineTo[x=326.0, y=5.0], MoveTo[x=333.0, y=1.0], LineTo[x=333.0, y=5.0], MoveTo[x=340.0, y=1.0], LineTo[x=340.0, y=5.0], MoveTo[x=347.0, y=1.0], LineTo[x=347.0, y=5.0], MoveTo[x=354.0, y=1.0], LineTo[x=354.0, y=5.0], MoveTo[x=360.0, y=1.0], LineTo[x=360.0, y=5.0], MoveTo[x=367.0, y=1.0], LineTo[x=367.0, y=5.0], MoveTo[x=374.0, y=1.0], LineTo[x=374.0, y=5.0], MoveTo[x=381.0, y=1.0], LineTo[x=381.0, y=5.0], MoveTo[x=388.0, y=1.0], LineTo[x=388.0, y=5.0], MoveTo[x=394.0, y=1.0], LineTo[x=394.0, y=5.0], MoveTo[x=401.0, y=1.0], LineTo[x=401.0, y=5.0], MoveTo[x=408.0, y=1.0], LineTo[x=408.0, y=5.0], MoveTo[x=415.0, y=1.0], LineTo[x=415.0, y=5.0], MoveTo[x=422.0, y=1.0], LineTo[x=422.0, y=5.0], MoveTo[x=428.0, y=1.0], LineTo[x=428.0, y=5.0], MoveTo[x=435.0, y=1.0], LineTo[x=435.0, y=5.0], MoveTo[x=442.0, y=1.0], LineTo[x=442.0, y=5.0]], fill=null, fillRule=NON_ZERO, stroke=0xc3c3c3ff, strokeWidth=1.0]

Text[text=«0,00», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«0,25», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«0,50», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«0,75», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«1,00», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«1,25», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«1,50», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«1,75», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«2,00», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«2,25», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«2,50», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«2,75», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«3,00», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«3,25», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

NumberAxis@5a503aa1[styleClass=axis]

Label@63297d6d[styleClass=label axis-label]''

Text[text="", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL_VERTICAL_CENTER, font=Font[name=System Regular, family=System, style=Regular, size=13.0], fontSmoothingType=LCD, fill=0x333333ff]

Path[elements=[MoveTo[x=21.0, y=319.0], LineTo[x=29.0, y=319.0], MoveTo[x=21.0, y=284.0], LineTo[x=29.0, y=284.0], MoveTo[x=21.0, y=248.0], LineTo[x=29.0, y=248.0], MoveTo[x=21.0, y=213.0], LineTo[x=29.0, y=213.0], MoveTo[x=21.0, y=177.0], LineTo[x=29.0, y=177.0], MoveTo[x=21.0, y=142.0], LineTo[x=29.0, y=142.0], MoveTo[x=21.0, y=106.0], LineTo[x=29.0, y=106.0], MoveTo[x=21.0, y=71.0], LineTo[x=29.0, y=71.0], MoveTo[x=21.0, y=35.0], LineTo[x=29.0, y=35.0], MoveTo[x=21.0, y=0.0], LineTo[x=29.0, y=0.0]], fill=null, fillRule=NON_ZERO, stroke=0xc3c3c3ff, strokeWidth=1.0]

Path[elements=[MoveTo[x=24.0, y=312.0], LineTo[x=28.0, y=312.0], MoveTo[x=24.0, y=305.0], LineTo[x=28.0, y=305.0], MoveTo[x=24.0, y=298.0], LineTo[x=28.0, y=298.0], MoveTo[x=24.0, y=291.0], LineTo[x=28.0, y=291.0], MoveTo[x=24.0, y=276.0], LineTo[x=28.0, y=276.0], MoveTo[x=24.0, y=269.0], LineTo[x=28.0, y=269.0], MoveTo[x=24.0, y=262.0], LineTo[x=28.0, y=262.0], MoveTo[x=24.0, y=255.0], LineTo[x=28.0, y=255.0], MoveTo[x=24.0, y=248.0], LineTo[x=28.0, y=248.0], MoveTo[x=24.0, y=241.0], LineTo[x=28.0, y=241.0], MoveTo[x=24.0, y=234.0], LineTo[x=28.0, y=234.0], MoveTo[x=24.0, y=227.0], LineTo[x=28.0, y=227.0], MoveTo[x=24.0, y=220.0], LineTo[x=28.0, y=220.0], MoveTo[x=24.0, y=206.0], LineTo[x=28.0, y=206.0], MoveTo[x=24.0, y=198.0], LineTo[x=28.0, y=198.0], MoveTo[x=24.0, y=191.0], LineTo[x=28.0, y=191.0], MoveTo[x=24.0, y=184.0], LineTo[x=28.0, y=184.0], MoveTo[x=24.0, y=170.0], LineTo[x=28.0, y=170.0], MoveTo[x=24.0, y=163.0], LineTo[x=28.0, y=163.0], MoveTo[x=24.0, y=156.0], LineTo[x=28.0, y=156.0], MoveTo[x=24.0, y=149.0], LineTo[x=28.0, y=149.0], MoveTo[x=24.0, y=135.0], LineTo[x=28.0, y=135.0], MoveTo[x=24.0, y=128.0], LineTo[x=28.0, y=128.0], MoveTo[x=24.0, y=121.0], LineTo[x=28.0, y=121.0], MoveTo[x=24.0, y=113.0], LineTo[x=28.0, y=113.0], MoveTo[x=24.0, y=99.0], LineTo[x=28.0, y=99.0], MoveTo[x=24.0, y=92.0], LineTo[x=28.0, y=92.0], MoveTo[x=24.0, y=85.0], LineTo[x=28.0, y=85.0], MoveTo[x=24.0, y=78.0], LineTo[x=28.0, y=78.0], MoveTo[x=24.0, y=64.0], LineTo[x=28.0, y=64.0], MoveTo[x=24.0, y=57.0], LineTo[x=28.0, y=57.0], MoveTo[x=24.0, y=50.0], LineTo[x=28.0, y=50.0], MoveTo[x=24.0, y=43.0], LineTo[x=28.0, y=43.0], MoveTo[x=24.0, y=28.0], LineTo[x=28.0, y=28.0], MoveTo[x=24.0, y=21.0], LineTo[x=28.0, y=21.0], MoveTo[x=24.0, y=14.0], LineTo[x=28.0, y=14.0], MoveTo[x=24.0, y=7.0], LineTo[x=28.0, y=7.0], MoveTo[x=24.0, y=0.0], LineTo[x=28.0, y=0.0]], fill=null, fillRule=NON_ZERO, stroke=0xc3c3c3ff, strokeWidth=1.0]

Text[text=«0,0», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«0,5», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«1,0», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«1,5», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«2,0», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«2,5», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«3,0», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«3,5», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«4,0», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Text[text=«4,5», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=10.800000190734863], fontSmoothingType=GRAY, fill=0x585858ff]

Legend@74020b4b[styleClass=chart-legend]

Label@1d725e97[styleClass=label chart-legend-item]'Temp1'

Region@15f4872[styleClass=chart-legend-item-symbol chart-line-symbol series0 default-color0]

Text[text=«Temp1», x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL_VERTICAL_CENTER, font=Font[name=System Regular, family=System, style=Regular, size=13.0], fontSmoothingType=LCD, fill=0x333333ff]

И проанализировав результат, можно легко понять, из каких компонентов состоит LineChart:

Гибкая настройка графиков в JavaFX

Поскольку своей целью я поставил замену всех узлов графика на картинку и создание всплывающих сообщений над каждом из них, то мне нужна часть, в которой находятся узлы.

Замена кружков на картинки

В обычном JavaFX, узлы графика представляются кружками с пустой серединой. Что бы узнать, как они создаются, мне пришлось немного полазить по коду LineChart (спасибо Oracle за открытые сырцы) и получил такой фрагмент кода:

private Node createSymbol(Series<X, Y> series, int seriesIndex, final Data<X,Y> item, int itemIndex) {
        Node symbol = item.getNode();
        // check if symbol has already been created
        if (symbol == null && getCreateSymbols()) {
            symbol = new StackPane();
            item.setNode(symbol);
        }
        // set symbol styles
        if (symbol != null) symbol.getStyleClass().addAll("chart-line-symbol", "series" + seriesIndex,
                "data" + itemIndex, series.defaultColorStyleClass);
        return symbol;
    }

Получается, узел представляет собой обычную панель, которая настроилась через CSS. Найдем CSS код, который используется для настройки узла (находится в /com/sun/javafx/scene/control/skin/caspian/caspian.css):

.chart-line-symbol {
    -fx-background-color: #f9d900, white;
    -fx-background-insets: 0, 2;
    -fx-background-radius: 5px;
    -fx-padding: 5px;
}

То есть, для изменение, достаточно переопеределить класс chart-line-symbol.
Создадим такой CSS файл:

.chart-line-symbol {
    -fx-background-image: url(%путь к картинке%);
}

И добавим этот файл к графику:

VBox vBox = new VBox();
LineChart chart = new LineChart(new NumberAxis(), new NumberAxis());
chart.getStylesheets().add(Main.class.getResource("Путь к css файлу").toExternalForm());
chart.getData().add(new XYChart.Series<>("Temp1", FXCollections.observableArrayList(new XYChart.Data<Object, Object>(1, 1), new XYChart.Data<Object, Object>(2, 2), new XYChart.Data<Object, Object>(3, 4))));
vBox.getChildren().add(chart);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
((XYChart.Series) chart.getData().get(0)).getData().addAll(new XYChart.Data<>(5, 5));

Полученный результат можно увидеть на первой картинке.

Всплывающие подсказки в узлах

Эту фичу добавить несколько сложнее, так как при помощи CSS это не получится.
Вернемся к той части кода, где в LineChart создавался символ:

private Node createSymbol(Series<X, Y> series, int seriesIndex, final Data<X,Y> item, int itemIndex) {
        Node symbol = item.getNode();
        // check if symbol has already been created
        if (symbol == null && getCreateSymbols()) {
            symbol = new StackPane();
            item.setNode(symbol);
        }
        // set symbol styles
        if (symbol != null) symbol.getStyleClass().addAll("chart-line-symbol", "series" + seriesIndex,
                "data" + itemIndex, series.defaultColorStyleClass);
        return symbol;
    }

Как можно заметить, дальше символ хранится в объекте Data<X,Y>, который задает точку графика.
То есть, можно просто пройтись по всем точкам в Series и для всех установить всплывающие подсказки:

ObservableList<XYChart.Data> dataList = ((XYChart.Series) chart.getData().get(0)).getData();
        dataList.forEach(data->{
            Node node = data.getNode();
            Tooltip tooltip = new Tooltip('('+data.getXValue().toString()+';'+data.getYValue().toString()+')');
            Tooltip.install(node, tooltip);
        });

После запуска наведите на узел графика и увидите результат.

Заключение

Надеюсь, у меня получилось показать способы настроек компонентов в JavaFX и их модификацию. Разумеется, тем, что я представил, возможности не ограничиваются. В частности, можно сделать модификацию LineChart (как поступил я), в которой можно будет динамически менять цвета, и которая будет добавлять всплывающие подсказки. Можно менять вид фона, сетки, осей и подписей. Вообщем, настраивать как душе угодно. Главное — помнить, что статические настройки необходимо выносить в CSS, а всю динамику, к несчастью, в код.

Автор: SirEdvin

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js