对齐多个XYCharts的X轴

时间:2018-03-06 20:39:43

标签: javafx layout

我想要垂直对齐显示两个XYChart

这两个图共享相同的x轴,但它们使用不同的数据集,这些数值不是相同的数量级。这使得y轴标签的尺寸大不相同。最后,两个x轴不再对齐。

我的目标是对齐那些x轴。

一个建议的解决方案通过旋转y轴标签提供解决方法,使标签宽度相同。虽然这是有效的(我感谢贡献者的回答),但这并不完全令人满意:只要我有不同大小的传说,我就会遇到同样的问题(参见R中的this question,哪里有几个令人满意的解决方案。)

我真的希望保持标签的方向不变,并且"播放"图表组件大小。

为了重现性,这里是一个由Oracle's tutorial构建的示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class Test extends Application {

    @Override
    public void start(Stage stage) {
        stage.setTitle("Line Chart Sample");
        final CategoryAxis xAxis1 = new CategoryAxis();
        final NumberAxis yAxis1 = new NumberAxis();
        xAxis1.setLabel("Month");
        final LineChart<String, Number> lineChart1 =
                new LineChart<>(xAxis1, yAxis1);

        final CategoryAxis xAxis2 = new CategoryAxis();
        xAxis2.setLabel("Month");
        final NumberAxis yAxis2 = new NumberAxis();
        final LineChart<String, Number> lineChart2 =
                new LineChart<>(xAxis2, yAxis2);

        lineChart1.setTitle("Charts");
        lineChart1.setLegendVisible(false);
        lineChart2.setLegendVisible(false);

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("Portfolio 1");

        series1.getData().add(new XYChart.Data("Jan", 23));
        series1.getData().add(new XYChart.Data("Feb", 14));
        series1.getData().add(new XYChart.Data("Mar", 15));
        series1.getData().add(new XYChart.Data("Apr", 24));
        series1.getData().add(new XYChart.Data("May", 34));
        series1.getData().add(new XYChart.Data("Jun", 36));
        series1.getData().add(new XYChart.Data("Jul", 22));
        series1.getData().add(new XYChart.Data("Aug", 45));
        series1.getData().add(new XYChart.Data("Sep", 43));
        series1.getData().add(new XYChart.Data("Oct", 17));
        series1.getData().add(new XYChart.Data("Nov", 29));
        series1.getData().add(new XYChart.Data("Dec", 25));

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("Portfolio 2");
        series2.getData().add(new XYChart.Data("Jan", 330000));
        series2.getData().add(new XYChart.Data("Feb", 340000));
        series2.getData().add(new XYChart.Data("Mar", 250000));
        series2.getData().add(new XYChart.Data("Apr", 440000));
        series2.getData().add(new XYChart.Data("May", 390000));
        series2.getData().add(new XYChart.Data("Jun", 160000));
        series2.getData().add(new XYChart.Data("Jul", 550000));
        series2.getData().add(new XYChart.Data("Aug", 540000));
        series2.getData().add(new XYChart.Data("Sep", 480000));
        series2.getData().add(new XYChart.Data("Oct", 270000));
        series2.getData().add(new XYChart.Data("Nov", 370000));
        series2.getData().add(new XYChart.Data("Dec", 290000));

        lineChart1.getData().addAll(series1);
        lineChart2.getData().addAll(series2);

        VBox vBox = new VBox();
        vBox.addAll(lineChart1, lineChart2);

        Scene scene = new Scene(gridPane, 800, 600);

        stage.setScene(scene);
        stage.show();
    }


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

}

结果如下:

enter image description here

是否可以控制图表组件的大小以使两个x轴对齐?

我试图改变首选的y轴和最小宽度,但它似乎不起作用。

我目前正在使用Java 8。如有必要,可以迁移到Java 9。

编辑:正如答案中所建议的,我已将此问题的范围缩小到对齐x轴的问题。问题调整问题是here

2 个答案:

答案 0 :(得分:3)

这里是一个专门的布局

的快速镜头
  • 只关心XYChart类型的孩子
  • 将它们垂直放置,并在它们之间具有可配置的间距
  • 将yAxis宽度调整为prefWidth,注意我们需要传递轴的实际高度(相对于典型的-1)...花了我一段时间......
  • 水平对齐,使xAxis对齐,yAxis有足够的空间用于标签

这不是生产质量,只是对于如何做的初学者:)

/**
 * Lays out XYCharts vertically such that their x-axis are aligned and
 * there's enough space to fully show the labels of all y-axis.
 *   
 * @author Jeanette Winzenburg, Berlin
 */
public class VChartBox extends Pane {

    protected void layoutChildren() {
        Insets insets = getInsets();
        double width = getWidth();
        double height = getHeight();
        // not entirely certain when to apply all the snaps, this is 
        // simply copied from vbox 
        double top = snapSpaceY(insets.getTop());
        double left = snapSpaceX(insets.getLeft());
        double bottom = snapSpaceY(insets.getBottom());
        double right = snapSpaceX(insets.getRight());
        double space = snapSpaceY(getSpacing());

        double availableWidth = snapSpaceX(width - left - right);
        List<XYChart> charts = getCharts();
        if (charts.isEmpty()) return;
        double heightPerChart = height / charts.size() - space;
        OptionalDouble maxYAxisWidth = charts.stream()
                .filter(chart -> chart.getYAxis() != null)
                .mapToDouble(chart -> chart.getYAxis().prefWidth(heightPerChart))
                .max();
        double maxYWidth = maxYAxisWidth.orElse(0);
        double remainingWidth = availableWidth - maxYWidth;
        for (XYChart c : charts) {
            Axis axis = c.getYAxis();
            double axisWidth = axis != null ? axis.prefWidth(heightPerChart) : 0;
            double axisOffset = maxYWidth - axisWidth;
            double xOffset = axisOffset + left;
            double chartWidth = remainingWidth + axisWidth;
            c.resizeRelocate(xOffset, top, chartWidth, heightPerChart);
            top += snapSpaceY(c.getHeight() + getSpacing());
        }
    }

    protected List<XYChart> getCharts() {
        return getChildren().stream().filter(child -> child instanceof XYChart)
                .map(chart -> (XYChart) chart).collect(toList());
    }

    // properties
    /**
     * The amount of vertical space between each child in the vbox.
     * 
     * @return the amount of vertical space between each child in the vbox
     */
    public final DoubleProperty spacingProperty() {
        if (spacing == null) {
            spacing = new SimpleDoubleProperty(this, "spacing", 20) {
                @Override
                public void invalidated() {
                    requestLayout();
                }

            };
        }
        return spacing;
    }

    private DoubleProperty spacing;

    public final void setSpacing(double value) {
        spacingProperty().set(value);
    }

    public final double getSpacing() {
        return spacing == null ? 0 : spacing.get();
    }

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(VChartBox.class.getName());
}

答案 1 :(得分:1)

删除GridPane代码并将其替换为:

VBox vBox = new VBox(lineChart1, lineChart2);
Scene scene = new Scene(vBox, 800, 600);
  

完整代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication139 extends Application
{

    @Override
    public void start(Stage primaryStage)
    {
        primaryStage.setTitle("Line Chart Sample");
        final CategoryAxis xAxis1 = new CategoryAxis();
        final NumberAxis yAxis1 = new NumberAxis();
        xAxis1.setLabel("Month");
        final LineChart<String, Number> lineChart1
                = new LineChart<>(xAxis1, yAxis1);

        final CategoryAxis xAxis2 = new CategoryAxis();
        xAxis2.setLabel("Month");
        final NumberAxis yAxis2 = new NumberAxis();
        final LineChart<String, Number> lineChart2
                = new LineChart<>(xAxis2, yAxis2);

        lineChart1.setTitle("Charts");
        lineChart1.setLegendVisible(false);
        lineChart1.getYAxis().setTickLabelRotation(270);
        lineChart2.setLegendVisible(false);
        lineChart2.getYAxis().setTickLabelRotation(270);

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("Portfolio 1");

        series1.getData().add(new XYChart.Data("Jan", 23));
        series1.getData().add(new XYChart.Data("Feb", 14));
        series1.getData().add(new XYChart.Data("Mar", 15));
        series1.getData().add(new XYChart.Data("Apr", 24));
        series1.getData().add(new XYChart.Data("May", 34));
        series1.getData().add(new XYChart.Data("Jun", 36));
        series1.getData().add(new XYChart.Data("Jul", 22));
        series1.getData().add(new XYChart.Data("Aug", 45));
        series1.getData().add(new XYChart.Data("Sep", 43));
        series1.getData().add(new XYChart.Data("Oct", 17));
        series1.getData().add(new XYChart.Data("Nov", 29));
        series1.getData().add(new XYChart.Data("Dec", 25));

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("Portfolio 2");
        series2.getData().add(new XYChart.Data("Jan", 330000));
        series2.getData().add(new XYChart.Data("Feb", 340000));
        series2.getData().add(new XYChart.Data("Mar", 250000));
        series2.getData().add(new XYChart.Data("Apr", 440000));
        series2.getData().add(new XYChart.Data("May", 390000));
        series2.getData().add(new XYChart.Data("Jun", 160000));
        series2.getData().add(new XYChart.Data("Jul", 550000));
        series2.getData().add(new XYChart.Data("Aug", 540000));
        series2.getData().add(new XYChart.Data("Sep", 480000));
        series2.getData().add(new XYChart.Data("Oct", 270000));
        series2.getData().add(new XYChart.Data("Nov", 370000));
        series2.getData().add(new XYChart.Data("Dec", 290000));

        lineChart1.getData().addAll(series1);
        lineChart2.getData().addAll(series2);

        VBox vBox = new VBox(lineChart1, lineChart2);
        Scene scene = new Scene(vBox, 800, 600);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}

enter image description here