在我的项目中,我创建了多个 AreaCharts,如下所示:
问题是显示图表的最大数量为 10,但 X 轴的长度可能会有所不同。在每个图表中,我使用 4 个系列:
- 一个用于黑色刻度
- 一个用于红色勾号
- 一个用于绿色刻度
- 发生数据的最后一个
问题是当图表的 xAxis 长度达到大约 1200 时,滚动 scrollPane 一点也不流畅,几乎无法滚动。
我的问题是:有没有办法让我的图表显示更高效?
编辑:布局层次结构如下所示:
滚动 Pane (垂直框(图表))
预计到达时间: 这是独立版本,显示平均效率,图表中显示的值数量更多。 注意:运行此程序可能需要几秒钟才能显示图表。我怎样才能使这些 View 的滚动更加顺畅?
程序类:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Program2 extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
try {
ScrollPane scrollPane = new ScrollPane();
VBox vBox = new VBox();
scrollPane.setContent(vBox);
Scene scene = new Scene(scrollPane, 600, 600);
primaryStage.setMaximized(true);
primaryStage.setScene(scene);
primaryStage.show();
for (int i = 0; i < 10; i++) {
CustomChart2 customChart2 = new CustomChart2();
AreaChart.Series<Number, Number> series = new AreaChart.Series<>();
for (int j = 0; j < 2500; j += i + 2) {
series.getData().add(new XYChart.Data<>(j, -0.2));
series.getData().add(new XYChart.Data<>(j, 1));
series.getData().add(new XYChart.Data<>(j + 1, 1));
series.getData().add(new XYChart.Data<>(j + 1, -0.2));
}
customChart2.getData().add(series);
AreaChart.Series<Number, Number> series2 = new AreaChart.Series<>();
for (int j = 0; j < 2500; j += i + 1) {
series2.getData().add(new XYChart.Data<>(j, -0.2));
series2.getData().add(new XYChart.Data<>(j, 1.5));
series2.getData().add(new XYChart.Data<>(j + 0.01, 1.5));
series2.getData().add(new XYChart.Data<>(j + 0.01, -0.2));
}
customChart2.getData().add(series2);
AreaChart.Series<Number, Number> series3 = new AreaChart.Series<>();
for (int j = 2; j < 2500; j += i + 1) {
series3.getData().add(new XYChart.Data<>(j, -0.2));
series3.getData().add(new XYChart.Data<>(j, 1.2));
series3.getData().add(new XYChart.Data<>(j + 0.01, 1.2));
series3.getData().add(new XYChart.Data<>(j + 0.01, -0.2));
}
customChart2.getData().add(series3);
vBox.getChildren().add(customChart2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
修改后的图表类:
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
public class CustomChart2 extends AreaChart<Number, Number> {
private NumberAxis xAxis;
private NumberAxis yAxis;
public CustomChart2() {
super(new NumberAxis(0, 2500, 5), new NumberAxis(0, 1.5, 1.5));
this.xAxis = (NumberAxis) getXAxis();
this.yAxis = (NumberAxis) getYAxis();
configure();
configureXAxis();
configureYAxis();
}
private void configure() {
setAnimated(false);
setCreateSymbols(false);
setMinWidth(2500 * 20);
computeMaxHeight(60);
setMaxHeight(60);
maxHeight(60);
setMinHeight(60);
setHorizontalGridLinesVisible(false);
setVerticalGridLinesVisible(false);
setLegendVisible(false);
setVerticalZeroLineVisible(false);
setHorizontalZeroLineVisible(false);
}
private void configureXAxis() {
xAxis.setAutoRanging(false);
}
private void configureYAxis() {
yAxis.setPrefSize(0, 0);
yAxis.setAutoRanging(false);
yAxis.setTickMarkVisible(false);
yAxis.setTickLabelsVisible(false);
yAxis.setMinorTickVisible(false);
}
}
最佳答案
您提到的性能问题有点奇怪:图像看起来不太复杂。没有看到 mcve 就很难提供任何建议。 .
如果您生成太多节点(数万个)并且无法减少该数量,您有一些选择:
- 使用 canvas基于图表库。一个示例库是 JFreeChart ,它可以绘制到 JavaFX Canvas 上。
- 预先生成图表并拍摄图像 snapshot放置在 ScrollPane 内。如果使用此方法,请确保 animation在图表上关闭。
- 使用 virtualized grid ,它只呈现当前可见的图表(网格单元格)。
其他general performance tips (例如打开节点缓存),可能有助于提高性能。
Virtualized grid seems to be a solution for my problem, BUT I don't see any options for horizontal scrolling so in this case it's useless
我从未使用过 ControlsFX GridView,但我猜它的工作方式与其他虚拟化控件(如 ListView 或 TableView)相同,它们会在必要时隐式显示适当的滚动条(例如,如果可用高度 >= 首选高度并且可用宽度 < 首选宽度,则不会显示垂直滚动,但会显示水平滚动)。因为滚动行为和显示是隐式的,所以在 GridView 的控件 API 上不需要任何选项。只是一个猜测。
更新
我尝试了 GridView,但不幸的是,它并不像我想的那么简单让水平滚动起作用。隐含地它只是进行垂直滚动,如果有一个 API 可以让水平滚动与当前实现一起工作(我怀疑),它没有记录并且使用起来不直观。添加或记录功能将是一个不错的 feature request请求 ControlsFX,但这现在对您没有帮助...所以也许坚持我的其他性能优化建议,或者想出更多您自己的或来自其他回答者的建议。
使用示例代码更新
所以我修改了 ControlsFX GridView 控件,使其可以使用水平滚动条。可能我做的方式不是它应该做的方式,但它似乎有效。
无论如何,这是一个在虚拟 GridView 中显示超过 9000 个图表的示例。 ChartGridCell 和 unpad-chart.css 源自对 How to make the chart content area take up the maximum area available to it? 的回答。除了一些测试代码和数据(它们并不是解决方案真正不可或缺的)之外,不要真正向这个答案添加任何有趣的东西。
示例代码对每个图表使用相同的系列数据,但您可以将系列数据放在 GridView 项目列表中,并在项目更新时更新图表(类似于图表标题和颜色以及在提供的样本)。
ManyChartsSample.java
import impl.org.controlsfx.skin.FixedWidthGridViewSkin;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.controlsfx.control.GridView;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.Random;
public class ManyChartsSample extends Application {
private static final double FIXED_GRID_WIDTH = 3_000;
private static final int OVER_9000 = 9001;
private static final Random r = new Random(42);
@Override
public void start(Stage stage) throws URISyntaxException, MalformedURLException {
GridView<ChartGridCell.CellData> grid = new GridView<>();
grid.setSkin(new FixedWidthGridViewSkin<>(grid, FIXED_GRID_WIDTH));
grid.setCellFactory(gridView -> new ChartGridCell());
grid.setHorizontalCellSpacing(5);
grid.setVerticalCellSpacing(5);
grid.setCellHeight(200);
grid.setCellWidth(200);
for(int i = 0; i < OVER_9000; i++) {
Color c1 = new Color(r.nextDouble(), r.nextDouble(), r.nextDouble(), 1.0);
Color c2 = new Color(r.nextDouble(), r.nextDouble(), r.nextDouble(), 1.0);
ChartGridCell.CellData data = new ChartGridCell.CellData(i, c1, c2);
grid.getItems().add(data);
}
grid.setPrefSize(300, 200);
Scene scene = new Scene(grid);
scene.getStylesheets().add(
getClass().getResource("unpad-chart.css").toURI().toURL().toExternalForm()
);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
impl.org.controlsfx.skin.FixedWidthGridViewSkin
package impl.org.controlsfx.skin;
import org.controlsfx.control.GridView;
public class FixedWidthGridViewSkin<T> extends GridViewSkin<T> {
private final double fixedWidth;
public FixedWidthGridViewSkin(GridView<T> control, double fixedWidth) {
super(control);
this.fixedWidth = fixedWidth;
}
@Override public GridRow<T> createCell() {
GridRow<T> row = new GridRow<>();
row.setPrefWidth(fixedWidth);
row.updateGridView(getSkinnable());
return row;
}
protected double computeRowWidth() {
return Math.max(getSkinnable().getWidth() - 18, fixedWidth);
}
}
ChartGridCell.java
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.paint.Color;
import org.controlsfx.control.GridCell;
public class ChartGridCell extends GridCell<ChartGridCell.CellData> {
private final AreaChart<Number,Number> chart;
public static class CellData {
private final int idx;
private final Color c1;
private final Color c2;
private final Color c1t;
private final Color c2t;
public CellData(int idx, Color c1, Color c2) {
this.idx = idx;
this.c1 = c1;
this.c2 = c2;
c1t = new Color(c1.getRed(), c1.getGreen(), c1.getBlue(), 0.2);
c2t = new Color(c2.getRed(), c2.getGreen(), c2.getBlue(), 0.2);
}
public int getIdx() {
return idx;
}
public Color getC1() {
return c1;
}
public Color getC2() {
return c2;
}
public Color getC1t() {
return c1t;
}
public Color getC2t() {
return c2t;
}
}
/**
* Creates a default ColorGridCell instance.
*/
public ChartGridCell() {
getStyleClass().add("chart-grid-cell"); //$NON-NLS-1$
final NumberAxis xAxis = new NumberAxis(1, 31, 1);
final NumberAxis yAxis = new NumberAxis(0, 28, 1);
xAxis.setAutoRanging(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickMarkVisible(false);
xAxis.setTickLabelsVisible(false);
xAxis.setPrefSize(0, 0);
yAxis.setAutoRanging(false);
yAxis.setMinorTickVisible(false);
yAxis.setTickMarkVisible(false);
yAxis.setTickLabelsVisible(false);
yAxis.setPrefSize(0, 0);
chart = new AreaChart<>(xAxis,yAxis);
chart.setHorizontalGridLinesVisible(false);
chart.setVerticalGridLinesVisible(false);
chart.setLegendVisible(false);
chart.setVerticalZeroLineVisible(false);
chart.setHorizontalZeroLineVisible(false);
chart.setAnimated(false);
XYChart.Series seriesApril= new XYChart.Series();
seriesApril.setName("April");
seriesApril.getData().add(new XYChart.Data(1, 4));
seriesApril.getData().add(new XYChart.Data(3, 10));
seriesApril.getData().add(new XYChart.Data(6, 15));
seriesApril.getData().add(new XYChart.Data(9, 8));
seriesApril.getData().add(new XYChart.Data(12, 5));
seriesApril.getData().add(new XYChart.Data(15, 18));
seriesApril.getData().add(new XYChart.Data(18, 15));
seriesApril.getData().add(new XYChart.Data(21, 13));
seriesApril.getData().add(new XYChart.Data(24, 19));
seriesApril.getData().add(new XYChart.Data(27, 21));
seriesApril.getData().add(new XYChart.Data(30, 21));
seriesApril.getData().add(new XYChart.Data(31, 19));
XYChart.Series seriesMay = new XYChart.Series();
seriesMay.setName("May");
seriesMay.getData().add(new XYChart.Data(1, 20));
seriesMay.getData().add(new XYChart.Data(3, 15));
seriesMay.getData().add(new XYChart.Data(6, 13));
seriesMay.getData().add(new XYChart.Data(9, 12));
seriesMay.getData().add(new XYChart.Data(12, 14));
seriesMay.getData().add(new XYChart.Data(15, 18));
seriesMay.getData().add(new XYChart.Data(18, 25));
seriesMay.getData().add(new XYChart.Data(21, 25));
seriesMay.getData().add(new XYChart.Data(24, 23));
seriesMay.getData().add(new XYChart.Data(27, 26));
seriesMay.getData().add(new XYChart.Data(31, 26));
chart.getData().addAll(seriesApril, seriesMay);
}
@Override protected void updateItem(ChartGridCell.CellData item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
chart.setTitle(item.getIdx() + ": " + toRGBCode(item.getC1()));
chart.setStyle(
"CHART_COLOR_1: " + toRGBCode(item.getC1()) + "; " +
"CHART_COLOR_1_TRANS_20: " + toRGBCode(item.getC1t()) + ";" +
"CHART_COLOR_2: " + toRGBCode(item.getC2()) + "; " +
"CHART_COLOR_2_TRANS_20: " + toRGBCode(item.getC2t()) + ";"
);
setGraphic(chart);
}
}
private String toRGBCode( Color color ) {
return String.format( "#%02X%02X%02X%02X",
(int)( color.getRed() * 255 ),
(int)( color.getGreen() * 255 ),
(int)( color.getBlue() * 255 ),
(int)( color.getOpacity() * 255 )
);
}
}
unpad-chart.css
.chart {
-fx-padding: 0px;
}
.chart-content {
-fx-padding: 0px;
}
.axis {
AXIS_COLOR: transparent;
}
.axis:top > .axis-label,
.axis:left > .axis-label {
-fx-padding: 0;
}
.axis:bottom > .axis-label,
.axis:right > .axis-label {
-fx-padding: 0;
}
关于Javafx AreaChart效率低——如何让滚动更流畅?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41129914/