我有一个swing应用程序,它的面板包含几个JavaFX AreaCharts(使用JavaFX.embed.swing.JFXPanel)和自定义样式。我们使用的是jre 8u20和jre 8u25,都很好,现在我必须更新到jre 8u 66,我的图表看起来不一样。
这个问题描述与我的情况相反:如何向JavaFx面积图添加负值?我使用图表系列根据数据为图表的背景着色(例如,对于不存在数据的轴 X 间隔,我需要在红色背景中着色)。JavaFX 8u20面积图背景被着色到图表线的下限,更新后面积图填充背景仅到轴忽略轴下或上方的部分。
现在JavaFX面积图的工作方式与文档中描述的一样:
我如何使用jre 8u66返回javaFX区域图表以获得类似的旧行为:
编辑:
所以我找到了修复负值背景填充 http://hg.openjdk.java.net/openjfx/8u60/rt/rev/a57b8ba039d0?revcount=480 提交。
这只是方法中的几行,我的第一个想法是快速修复:在我自己的类中覆盖这个方法,但我在这样做时遇到了问题,JavaFX类对这种修改不友好,许多必需的字段和方法是私有的或包私有的:(
以下是我试图改变区域图行为的类:
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());
}
}
}
}
问题是:
>
原始区域图的字段是私人的,无法访问
私有地图,DoubleProperty
有些方法是包私有的,例如
javafx.scene.chart.XYChart.Data.getCurrentX()
javafx.scene.chart.XYChart.Data.getCurrentY()
javafx.scene.chart.XYChart.getDataSize()
因此,作为可能的解决方法,我制作了这个:
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;
}
}
修复后,一切看起来都像它应该的那样(与上一张图片相同的数据集):
面积图包括普通的面积图(area)及面积范围图(arearange),根据面积连接线的不同,又可以分为直线面积图(area、arearange)和曲线面积图(areaspline 及 areasplinerange)。 一、面积图 图4-3 Highcharts 面积图 面积图相关的配置参考 API 文档: 面积图配置:针对当前数据列有效 面积数据列配置 :针对当前页面的所有面积数据列有效 1、曲
基本上,我有一个方法可以将Image从数据库加载到ImageView中,还有第二个方法可以更改图像,我成功地运行了这两个方法,没有出现异常,但是在change eImage()方法中的setImage之后,我需要更新什么以及如何(场景、阶段)是可能的。我知道在javafx中没有像swings中的repaint()这样的方法,那么我该如何处理呢?
$config = ['path' => './tests']; $fileObject = new \Vtiful\Kernel\Excel($config); $fileObject = $fileObject->fileName('tutorial.xlsx'); $fileHandle = $fileObject->getHandle(); $chart = new \Vtif
我正在寻找一种方法,在运行时改变JavaFX FXML应用程序的视图,并保存结果,以便在下次应用程序启动时显示。有办法吗?我在网上搜索,但没有找到任何解决方案!例如:我有一个函数,可以在GridPane中添加标签,并保存新添加的标签。在我的应用程序下一次启动时,新标签出现了。这是一个数据库应用程序,用户可以为自定义数据添加新的数据库字段。
当我试图用NetBeans为JavaFX原生打包EXE文件更改桌面图标时,Ant阻止我更改桌面图标。一旦将图标附加到项目名称,该图标将永远保持不变。我甚至尝试删除项目并重用项目名称。我在Tools->Options->Java->Ant中看到了要求添加Classpath的建议,所以我尝试添加了这一行c:\use\username\documents\workspace\projectname\sr
}`我正在计划在同一舞台上使不同的部分成为自己的场景。如果有任何帮助,我将不胜感激。我正在使用NetBeans8.2。