【问题标题】:JavaFX area chart behavior changedJavaFX 面积图行为已更改
【发布时间】:2015-11-13 14:30:14
【问题描述】:

我有一个摆动应用程序,它的面板包含几个具有自定义样式的 JavaFX AreaCharts(使用 javafx.embed.swing.JFXPanel)。我们使用了 jre 8u20 和 jre 8u25 并且一切正常,现在我必须更新到 jre 8u66,我的图表看起来不同了。

这个问题与我的情况相反:How to add negative values to JavaFx Area Chart?。我已经使用图表系列根据数据为图表的背景着色(例如,我需要为不存在数据的轴 X 间隔着色为红色背景)。 JavaFX 8u20 区域图背景被着色到图表线的下限,更新区域图后仅填充背景到轴忽略轴下方或上方的部分。

现在 JavaFX 面积图的工作方式与文档中描述的一样:

如何返回 javaFX 区域图表的旧行为以使用 jre 8u66 获得类似的东西:

编辑:

所以我找到了修复负值背景填充http://hg.openjdk.java.net/openjfx/8u60/rt/rev/a57b8ba039d0?revcount=480 的提交。

方法中只有几行,我的第一个想法是快速修复:在我自己的类中覆盖这个方法,但是这样做有问题,JavaFX 类对许多需要的修改不友好字段和方法是私有的或包私有的:(

这是我试图改变 AreaChart 行为的类:

public class NegativeBGAreaChart<X,Y> extends AreaChart<X, Y> {
    public NegativeBGAreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis) {
        this(xAxis,yAxis, FXCollections.<Series<X,Y>>observableArrayList());
    }

    public NegativeBGAreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X,Y>> data) {
        super(xAxis,yAxis, data);
    }


    @Override 
    protected void layoutPlotChildren() {
        List<LineTo> constructedPath = new ArrayList<>(getDataSize());
        for (int seriesIndex=0; seriesIndex < getDataSize(); seriesIndex++) {
            Series<X, Y> series = getData().get(seriesIndex);
            DoubleProperty seriesYAnimMultiplier = seriesYMultiplierMap.get(series);
            double lastX = 0;
            final ObservableList<Node> children = ((Group) series.getNode()).getChildren();
            ObservableList<PathElement> seriesLine = ((Path) children.get(1)).getElements();
            ObservableList<PathElement> fillPath = ((Path) children.get(0)).getElements();
            seriesLine.clear();
            fillPath.clear();
            constructedPath.clear();
            for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext(); ) {
                Data<X, Y> item = it.next();
                double x = getXAxis().getDisplayPosition(item.getCurrentX());
                double y = getYAxis().getDisplayPosition( getYAxis().toRealValue(getYAxis().toNumericValue(item.getCurrentY()) * seriesYAnimMultiplier.getValue()));
                constructedPath.add(new LineTo(x, y));
                if (Double.isNaN(x) || Double.isNaN(y)) {
                    continue;
                }
                lastX = x;
                Node symbol = item.getNode();
                if (symbol != null) {
                    final double w = symbol.prefWidth(-1);
                    final double h = symbol.prefHeight(-1);
                    symbol.resizeRelocate(x-(w/2), y-(h/2),w,h);
                }
            }

            if (!constructedPath.isEmpty()) {
                Collections.sort(constructedPath, (e1, e2) -> Double.compare(e1.getX(), e2.getX()));
                LineTo first = constructedPath.get(0);

                final double displayYPos = first.getY();
                final double numericYPos = getYAxis().toNumericValue(getYAxis().getValueForDisplay(displayYPos));

                // RT-34626: We can't always use getZeroPosition(), as it may be the case
                // that the zero position of the y-axis is not visible on the chart. In these
                // cases, we need to use the height between the point and the y-axis line.
                final double yAxisZeroPos = getYAxis().getZeroPosition();
                final boolean isYAxisZeroPosVisible = !Double.isNaN(yAxisZeroPos);
                final double yAxisHeight = getYAxis().getHeight();
                final double yFillPos = isYAxisZeroPosVisible ? yAxisZeroPos : numericYPos < 0 ? numericYPos - yAxisHeight : yAxisHeight;

                seriesLine.add(new MoveTo(first.getX(), displayYPos));
                fillPath.add(new MoveTo(first.getX(), yFillPos));

                seriesLine.addAll(constructedPath);
                fillPath.addAll(constructedPath);
                fillPath.add(new LineTo(lastX, yFillPos));
                fillPath.add(new ClosePath());
            }
        }
    }
}

问题是:

  1. 原始 AreaChart 的字段是私有且不可访问的

    私有映射,DoubleProperty> seriesYMultiplierMap = new HashMap();

  2. 有些方法是包私有的,例如

    javafx.scene.chart.XYChart.Data.getCurrentX()

    javafx.scene.chart.XYChart.Data.getCurrentY()

    javafx.scene.chart.XYChart.getDataSize()

【问题讨论】:

  • 您面临的问题很可能是由于最近对 JDK 所做的修复 - Area Chart with negative values。只需删除您对图表所做的任何自定义着色,检查输出。如果它仍然不是你想要的,你将不得不重新编写它。如果您仍然无法解决它,请编辑您的问题以向我们展示一些代码(或 css)。

标签: java javafx javafx-8


【解决方案1】:

所以我尽可能地解决了这个问题:

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javafx.beans.NamedArg;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.Axis;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;

/**
 * AreaChart - Plots the area between the line that connects the data points     and
 * the 0 line on the Y axis. This implementation Plots the area between the line
 * that connects the data points and the bottom of the chart area.
 * 
 * @since JavaFX 2.0
 */
public class NegativeBGAreaChart<X, Y> extends AreaChart<X, Y> {
    protected Map<Series<X, Y>, DoubleProperty> shadowSeriesYMultiplierMap = new HashMap<>();

    // -------------- CONSTRUCTORS ----------------------------------------------

    public NegativeBGAreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis) {
        this(xAxis, yAxis, FXCollections.<Series<X, Y>> observableArrayList());
    }

    public NegativeBGAreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X, Y>> data) {
        super(xAxis, yAxis, data);
    }

    // -------------- METHODS ------------------------------------------------------------------------------------------
    @Override
    protected void seriesAdded(Series<X, Y> series, int seriesIndex) {
        DoubleProperty seriesYAnimMultiplier = new SimpleDoubleProperty(this, "seriesYMultiplier");
        shadowSeriesYMultiplierMap.put(series, seriesYAnimMultiplier);
        super.seriesAdded(series, seriesIndex);
    }

    @Override
    protected void seriesRemoved(final Series<X, Y> series) {
        shadowSeriesYMultiplierMap.remove(series);
        super.seriesRemoved(series);
    }

    @Override
    protected void layoutPlotChildren() {
        // super.layoutPlotChildren();
        try {
            List<LineTo> constructedPath = new ArrayList<>(getDataSize());
            for (int seriesIndex = 0; seriesIndex < getDataSize(); seriesIndex++) {
                Series<X, Y> series = getData().get(seriesIndex);
                DoubleProperty seriesYAnimMultiplier = shadowSeriesYMultiplierMap.get(series);
                double lastX = 0;
                final ObservableList<Node> children = ((Group) series.getNode()).getChildren();
                ObservableList<PathElement> seriesLine = ((Path) children.get(1)).getElements();
                ObservableList<PathElement> fillPath = ((Path) children.get(0)).getElements();
                seriesLine.clear();
                fillPath.clear();
                constructedPath.clear();
                for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext();) {
                    Data<X, Y> item = it.next();
                    double x = getXAxis().getDisplayPosition(item.getXValue());// FIXME: here should be used item.getCurrentX()
                    double y = getYAxis().getDisplayPosition(
                        getYAxis().toRealValue(
                                getYAxis().toNumericValue(item.getYValue()) * seriesYAnimMultiplier.getValue()));// FIXME: here should be used item.getCurrentY()
                    constructedPath.add(new LineTo(x, y));
                    if (Double.isNaN(x) || Double.isNaN(y)) {
                        continue;
                    }
                    lastX = x;
                    Node symbol = item.getNode();
                    if (symbol != null) {
                        final double w = symbol.prefWidth(-1);
                        final double h = symbol.prefHeight(-1);
                        symbol.resizeRelocate(x - (w / 2), y - (h / 2), w, h);
                    }
                }

                if (!constructedPath.isEmpty()) {
                    Collections.sort(constructedPath, (e1, e2) -> Double.compare(e1.getX(), e2.getX()));
                    LineTo first = constructedPath.get(0);

                    seriesLine.add(new MoveTo(first.getX(), first.getY()));
                    fillPath.add(new MoveTo(first.getX(), getYAxis().getHeight()));

                    seriesLine.addAll(constructedPath);
                    fillPath.addAll(constructedPath);
                    fillPath.add(new LineTo(lastX, getYAxis().getHeight()));
                    fillPath.add(new ClosePath());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Gets the size of the data returning 0 if the data is null
     *
     * @return The number of items in data, or null if data is null
     */
    public int getDataSize() {
        final ObservableList<Series<X, Y>> data = getData();
        return (data != null) ? data.size() : 0;
    }
}

我知道它有问题,但我现在别无选择,我希望有一天 JavaFX 会被更改为对外部更改更友好。

编辑:

此解决方法正在解决所描述的问题,但似乎由于标记为 FIXME 的代码,系列点出现在错误的坐标上。在下面的屏幕截图中,系列点的 Y 坐标为 3 或 -3,但它们都位于坐标为 0 的轴上。

编辑2:

所以我已经设法修复它,动画有问题,所以我禁用了图表的动画:

chart.setAnimated(false);

并修复了方法(在第二个 FIXME 行中删除了动画乘数)所以最后我有了这个:

public class NegativeBGAreaChart<X, Y> extends AreaChart<X, Y> {
    protected Map<Series<X, Y>, DoubleProperty> shadowSeriesYMultiplierMap = new HashMap<>();

    // -------------- CONSTRUCTORS ----------------------------------------------

    public NegativeBGAreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis) {
        this(xAxis, yAxis, FXCollections.<Series<X, Y>> observableArrayList());
    }

    public NegativeBGAreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X, Y>> data) {
        super(xAxis, yAxis, data);
    }

    // -------------- METHODS ------------------------------------------------------------------------------------------
    @Override
    protected void seriesAdded(Series<X, Y> series, int seriesIndex) {
        DoubleProperty seriesYAnimMultiplier = new SimpleDoubleProperty(this, "seriesYMultiplier");
        shadowSeriesYMultiplierMap.put(series, seriesYAnimMultiplier);
        super.seriesAdded(series, seriesIndex);
    }

    @Override
    protected void seriesRemoved(final Series<X, Y> series) {
        shadowSeriesYMultiplierMap.remove(series);
        super.seriesRemoved(series);
    }

    @Override
    protected void layoutPlotChildren() {
//          super.layoutPlotChildren();
        try {
            List<LineTo> constructedPath = new ArrayList<>(getDataSize());
            for (int seriesIndex = 0; seriesIndex < getDataSize(); seriesIndex++) {
                Series<X, Y> series = getData().get(seriesIndex);
                DoubleProperty seriesYAnimMultiplier = shadowSeriesYMultiplierMap.get(series);
                double lastX = 0;
                final ObservableList<Node> children = ((Group) series.getNode()).getChildren();
                ObservableList<PathElement> seriesLine = ((Path) children.get(1)).getElements();
                ObservableList<PathElement> fillPath = ((Path) children.get(0)).getElements();
                seriesLine.clear();
                fillPath.clear();
                constructedPath.clear();
                for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext();) {
                    Data<X, Y> item = it.next();
                    double x = getXAxis().getDisplayPosition(item.getXValue());// FIXME: here should be used item.getCurrentX()
                    double y = getYAxis().getDisplayPosition(getYAxis().toRealValue(getYAxis().toNumericValue(item.getYValue())));// FIXME: here should be used item.getCurrentY()
                    constructedPath.add(new LineTo(x, y));
                    if (Double.isNaN(x) || Double.isNaN(y)) {
                        continue;
                    }
                    lastX = x;
                    Node symbol = item.getNode();
                    if (symbol != null) {
                        final double w = symbol.prefWidth(-1);
                        final double h = symbol.prefHeight(-1);
                        symbol.resizeRelocate(x - (w / 2), y - (h / 2), w, h);
                    }
                }

                if (!constructedPath.isEmpty()) {
                    Collections.sort(constructedPath, (e1, e2) -> Double.compare(e1.getX(), e2.getX()));
                    LineTo first = constructedPath.get(0);

                    seriesLine.add(new MoveTo(first.getX(), first.getY()));
                    fillPath.add(new MoveTo(first.getX(), getYAxis().getHeight()));

                    seriesLine.addAll(constructedPath);
                    fillPath.addAll(constructedPath);
                    fillPath.add(new LineTo(lastX, getYAxis().getHeight()));
                    fillPath.add(new ClosePath());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Gets the size of the data returning 0 if the data is null
     *
     * @return The number of items in data, or null if data is null
     */
    public int getDataSize() {
        final ObservableList<Series<X, Y>> data = getData();
        return (data != null) ? data.size() : 0;
    }
}

修复后一切看起来都正常(与上一张图片相同的数据集):

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-11
    • 2020-10-22
    • 2018-03-04
    • 2014-01-02
    • 2014-10-15
    相关资源
    最近更新 更多