【问题标题】:Aligning the X axes of several XYCharts对齐几个 XYCharts 的 X 轴
【发布时间】:2018-03-06 20:39:43
【问题描述】:

我有两个 XYCharts 我想垂直对齐显示。

这两个图共享相同的 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);
    }

}

结果如下:

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

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

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

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

【问题讨论】:

  • hmm ...我会尝试一个自定义布局,它知道它的子元素是图表并将它们布置成a)xAxis对齐并且b)前导“填充”足够宽最宽的 yAxis(包括标签)
  • 任何指示从哪里开始将不胜感激

标签: javafx layout


【解决方案1】:

这是一个专门的布局的快速快照

  • 只关心 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());
}

【讨论】:

    【解决方案2】:

    去掉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);
        }
    
    }
    

    【讨论】:

    • 每个线程只能问一个问题。在您自己很好地尝试解决问题后,开始一个新的问题来解决您的其他问题。
    • Y-Axis 范围设置为相同的数字应该可以解决排队问题。
    • 是的,但最上面的图应该是一条直线
    • 确实如此。
    • 看看它们旋转 90 度后会发生什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-08
    相关资源
    最近更新 更多