简体   繁体   English

为什么在XYChart中轴的父节点不是图表本身? (JavaFX)

[英]Why is the parent node of an axis in an XYChart not the chart itself? (JavaFX)

I have been trying to do some work with XYCharts, specifically placing things on or around them. 我一直在尝试使用XYCharts做一些工作,特别是在它们上面或周围放置东西。 I have been needing to use the coordinates of the bounding boxes of the axes in relation to the coordinate space defined by the whole chart. 我一直需要相对于整个图表定义的坐标空间使用轴的边界框的坐标。 However, I have realised that the .getBoundsInParent() method is not returning what I expect when applied to the axes. 但是,我已经意识到.getBoundsInParent()方法不会返回应用于轴的期望值。 It seems to be because the axis parent isn't the chart and I don't know what the parent is. 似乎是因为轴的父级不是图表,并且我不知道父级是什么。 For example the following boolean is false which to me seems like terrible design (assume chart is an instance of LineChart): 例如,以下布尔值是false,对我来说这似乎是糟糕的设计(假设图表是LineChart的一个实例):

chart.getXAxis().getParent() == chart chart.getXAxis()。getParent()==图表

what is going on here? 这里发生了什么? Thanks. 谢谢。

There's no direct API to get the bounds of the chart plot area. 没有直接的API可以获取图表绘制区域的边界。 One approach would be to use lookups, but that is not very robust. 一种方法是使用查找,但这不是很可靠。

If you want additional content in the chart plot area , the recommended approach is to override the chart's layoutPlotChildren method, as in this question . 如果要在图表绘图区域中添加其他内容,建议的方法是覆盖图表的layoutPlotChildren方法,如本问题所述

Probably the best approach for adding content outside the chart area, is to subclass Region and override the layoutChildren method to layout the chart and the extra content you want. 在图表区域外添加内容的最佳方法可能是对Region进行子类化,并重写layoutChildren方法以对图表和所需的额外内容进行布局。 You can determine coordinates for a specific "chart value" by using ValueAxis.getDisplayPosition(value) to get the coordinates relative to the axis, and then use a couple of transformations to get those coordinates relative to the region. 您可以通过使用ValueAxis.getDisplayPosition(value)来获取相对于轴的坐标,然后使用几个转换来获取相对于区域的坐标,从而确定特定“图表值”的坐标。

Here is a quick example: 这是一个简单的示例:

import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class LineChartWithRightPanel extends Application {

    private final Color[] RECT_COLORS = 
            new Color[] {Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE};
    private final double RECT_WIDTH = 20 ;

    @Override
    public void start(Stage primaryStage) {

        NumberAxis xAxis = new NumberAxis();
        NumberAxis yAxis = new NumberAxis(0, 1, 0.2);
        LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis);

        Region chartWithPanel = new Region() {

            private List<Rectangle> rectangles = 
                    Stream.of(RECT_COLORS)
                    .map(c -> new Rectangle(RECT_WIDTH, 0, c))
                    .collect(Collectors.toList());

            {
                getChildren().add(chart);
                getChildren().addAll(rectangles);
            }

            @Override
            protected void layoutChildren() {
                double w = getWidth();
                double h = getHeight();
                chart.resizeRelocate(0, 0, w-RECT_WIDTH, h);
                chart.layout();

                double chartMinY = yAxis.getLowerBound();
                double chartMaxY = yAxis.getUpperBound();
                double chartYRange = chartMaxY - chartMinY ;

                for (int i = 0 ; i < rectangles.size(); i++) {

                    // easier ways to do this in this example, 
                    // but this technique shows how to map "chart coords"
                    // to coords in this pane:

                    double rectMinYInChart = chartMinY + i * chartYRange / rectangles.size();
                    double rectMaxYInChart = chartMinY + (i+1) * chartYRange / rectangles.size();
                    double rectMinYInAxis = yAxis.getDisplayPosition(rectMinYInChart);
                    double rectMaxYInAxis = yAxis.getDisplayPosition(rectMaxYInChart);
                    double rectMinY = sceneToLocal(yAxis.localToScene(new Point2D(0, rectMinYInAxis))).getY();
                    double rectMaxY = sceneToLocal(yAxis.localToScene(new Point2D(0, rectMaxYInAxis))).getY();

                    rectangles.get(i).setHeight(Math.abs(rectMaxY - rectMinY));
                    rectangles.get(i).setX(w - RECT_WIDTH);
                    rectangles.get(i).setY(Math.min(rectMinY, rectMaxY));

                    Tooltip.install(rectangles.get(i), 
                            new Tooltip(String.format("%.2f - %.2f", rectMinYInChart, rectMaxYInChart)));
                }
            }
        };

        Series<Number, Number> series = new Series<>();
        series.setName("Data");
        Random rng = new Random();
        for (int i = 0; i < 20; i++) {
            series.getData().add(new Data<>(i, rng.nextDouble()));
        }
        chart.getData().add(series);

        BorderPane root = new BorderPane(chartWithPanel);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

带有彩色带区的图表的屏幕截图

Note that you can get the bounds of the entire plot area using the same idea: 请注意,您可以使用相同的思路获得整个绘图区域的边界:

double minXInAxis = xAxis.getDisplayPosition(xAxis.getLowerBound());
double maxXInAxis = xAxis.getDisplayPosition(xAxis.getUpperBound());
double minYInAxis = yAxis.getDisplayPosition(yAxis.getLowerBound());
double maxYInAxis = yAxis.getDisplayPosition(yAxis.getUpperBound());

double minX = sceneToLocal(xAxis.localToScene(new Point2D(minXInAxis,0))).getX();
double maxX = sceneToLocal(xAxis.localToScene(new Point2D(maxXInAxis,0))).getX();
double minY = sceneToLocal(yAxis.localToScene(new Point2D(0,minYInAxis))).getY();
double maxY = sceneToLocal(yAxis.localToScene(new Point2D(0,maxYInAxis))).getY();

Then minX , maxX , minY , maxY define the bounds of the plot area. 然后, minXmaxXminYmaxY定义绘图区域的边界。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM