CrosshairOverlay 使用 CombinedXYPlot (jfreechart) 抛出 NullpointerException
CrosshairOverlay throws NullpointerException with CombinedXYPlot (jfreechart)
我想从 jFreeCharts 库 添加一个 CrosshairOverlay
到我的 CombinedXYPlot
。在第一步中,我绘制了一个 XYPlot 并将其添加到我的组合 XYPlot 中。就像在几个演示和教程中一样,我创建了 crosshairOverlay 并将其添加到具有相应组合 XYPlot 的 chartPanel 中。
这是我的示例代码,它会产生 NullPointerException:
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.panel.CrosshairOverlay;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.Crosshair;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import javax.swing.*;
import java.awt.*;
public class stack implements ChartMouseListener{
CombinedDomainXYPlot combinedDomainXYPlot;
XYPlot myPlot1;
Crosshair xCrosshair;
Crosshair yCrosshair;
public static void main(String[] args){
stack s = new stack();
}
public stack(){
ApplicationFrame app = new ApplicationFrame("Example");
myPlot1 = createPlot();
combinedDomainXYPlot = createCombinedXYPlot();
JFreeChart chart = new JFreeChart("Example", combinedDomainXYPlot);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.addChartMouseListener(this);
CrosshairOverlay crosshairOverlay = new CrosshairOverlay();
this.xCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
this.xCrosshair.setLabelVisible(false);
this.yCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
this.yCrosshair.setLabelVisible(false);
crosshairOverlay.addDomainCrosshair(xCrosshair);
crosshairOverlay.addRangeCrosshair(yCrosshair);
//chartPanel.addOverlay(crosshairOverlay);
LegendTitle legend = chart.getLegend();
legend.setPosition(RectangleEdge.RIGHT);
legend.setBackgroundPaint(Color.BLACK);
legend.setItemPaint(Color.GRAY);
legend.setItemFont(new Font("Arial", 1, 9));
app.getContentPane().add(chartPanel);
app.pack();
app.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
app.setVisible(true);
}
private XYPlot createPlot(){
XYDataset data1 = createDataset();
XYItemRenderer renderer1 = new StandardXYItemRenderer();
NumberAxis rangeAxis1 = new NumberAxis("Range 1");
XYPlot myPlot = new XYPlot(data1,null,rangeAxis1,renderer1);
myPlot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
return myPlot;
}
private CombinedDomainXYPlot createCombinedXYPlot(){
CombinedDomainXYPlot combinedDomainXYPlot = new CombinedDomainXYPlot(new NumberAxis());
combinedDomainXYPlot.setGap(5);
combinedDomainXYPlot.add(myPlot1,11);
return combinedDomainXYPlot;
}
private XYDataset createDataset() {
// create dataset 2...
final XYSeries series2 = new XYSeries("Series 2");
series2.add(10.0, 16853.2);
series2.add(20.0, 19642.3);
series2.add(30.0, 18253.5);
series2.add(40.0, 15352.3);
series2.add(50.0, 13532.0);
series2.add(100.0, 12635.3);
series2.add(110.0, 13998.2);
series2.add(120.0, 11943.2);
series2.add(130.0, 16943.9);
series2.add(140.0, 17843.2);
series2.add(150.0, 16495.3);
series2.add(160.0, 17943.6);
series2.add(170.0, 18500.7);
series2.add(180.0, 19595.9);
return new XYSeriesCollection(series2);
}
@Override
public void chartMouseClicked(ChartMouseEvent event) {
// would be happy to get this called without exception
System.out.print("Test");
}
@Override
public void chartMouseMoved(ChartMouseEvent event) {
// would be happy to get this called without exception
System.out.print("Test");
}
}
异常:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at org.jfree.chart.panel.CrosshairOverlay.paintOverlay(CrosshairOverlay.java:257)
at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1658)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
at javax.swing.JComponent.paint(JComponent.java:1042)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
at java.awt.Container.paint(Container.java:1975)
at java.awt.Window.paint(Window.java:3904)
at javax.swing.RepaintManager.run(RepaintManager.java:842)
...
我可以在声明中找到这一点(在 CrosshairOverlay.java)
double yy = yAxis.valueToJava2D(y, dataArea, yAxisEdge);
抛出 NullPointerException 因为 yAxis
为空。该变量在上面的语句中赋值:
ValueAxis yAxis = plot.getRangeAxis();
函数 RangeAxis() 来自 XYPlot.java:
public ValueAxis getRangeAxis(int index) {
ValueAxis result = this.rangeAxes.get(index);
if (result == null) {
Plot parent = getParent();
if (parent instanceof XYPlot) {
XYPlot xy = (XYPlot) parent;
result = xy.getRangeAxis(index);
}
}
return result;
}
这里的parent不是XYPlot的实例,而是null。 getParent()
函数的注释说:
Returns the parent plot (or null
if this plot is not part
of a combined plot).
在这一点上我不明白为什么它 returns null
(也许是因为我在 CombinedXYPlot 中并且它没有父级?)。谁能帮我解释为什么父情节为空,以及如何解决我的问题?
我找到了问题的答案。我不得不通过修改 paintOverlay
函数来编写自己的 CrosshairOverlay
。现在(仅)为添加到 CombinedXYPlot
的第一个图画了十字准线
我不得不向下转换为 CombinedXYPlot
来获得一个函数,该函数为我提供了 XYPlot 子图的列表。从那以后,我选择了第一个子图,让十字准线绘画用这个来计算。
@Override
public void paintOverlay(Graphics2D g2, ChartPanel chartPanel) {
Shape savedClip = g2.getClip();
Rectangle2D dataArea = chartPanel.getScreenDataArea();
g2.clip(dataArea);
JFreeChart chart = chartPanel.getChart();
XYPlot plot = (XYPlot) chart.getPlot();
ValueAxis xAxis = plot.getDomainAxis();
RectangleEdge xAxisEdge = plot.getDomainAxisEdge();
Iterator iterator = getDomainCrosshairs().iterator();
while (iterator.hasNext()) {
Crosshair ch = (Crosshair) iterator.next();
if (ch.isVisible()) {
double x = ch.getValue();
double xx = xAxis.valueToJava2D(x, dataArea, xAxisEdge);
if (plot.getOrientation() == PlotOrientation.VERTICAL) {
drawVerticalCrosshair(g2, dataArea, xx, ch);
}
else {
drawHorizontalCrosshair(g2, dataArea, xx, ch);
}
}
}
// get subplots
List<XYPlot> subplots = ((CombinedDomainXYPlot) plot).getSubplots();
//take yAxis from first plot
ValueAxis yAxis = subplots.get(0).getRangeAxis();
RectangleEdge yAxisEdge = plot.getRangeAxisEdge();
iterator = this.getRangeCrosshairs().iterator();
while (iterator.hasNext()) {
Crosshair ch = (Crosshair) iterator.next();
if (ch.isVisible()) {
double y = ch.getValue();
double yy = yAxis.valueToJava2D(y, dataArea, yAxisEdge);
if (plot.getOrientation() == PlotOrientation.VERTICAL) {
drawHorizontalCrosshair(g2, dataArea, yy, ch);
}
else {
drawVerticalCrosshair(g2, dataArea, yy, ch);
}
}
}
g2.setClip(savedClip);
}
该解决方案的问题是,如果我向 CombinedXYPlot
添加子图, 十字准线位置将按每个子图的大小移动 。
我必须找到一种方法来获取子图的大小(每个图应该相同)并将其添加到 yy
位置的计算中。
这个问题与我原来的问题没有任何关系,所以我会等待帮助或等待更好的答案一段时间,并将此 post 标记为解决方案。
我想从 jFreeCharts 库 添加一个 CrosshairOverlay
到我的 CombinedXYPlot
。在第一步中,我绘制了一个 XYPlot 并将其添加到我的组合 XYPlot 中。就像在几个演示和教程中一样,我创建了 crosshairOverlay 并将其添加到具有相应组合 XYPlot 的 chartPanel 中。
这是我的示例代码,它会产生 NullPointerException:
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.panel.CrosshairOverlay;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.Crosshair;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import javax.swing.*;
import java.awt.*;
public class stack implements ChartMouseListener{
CombinedDomainXYPlot combinedDomainXYPlot;
XYPlot myPlot1;
Crosshair xCrosshair;
Crosshair yCrosshair;
public static void main(String[] args){
stack s = new stack();
}
public stack(){
ApplicationFrame app = new ApplicationFrame("Example");
myPlot1 = createPlot();
combinedDomainXYPlot = createCombinedXYPlot();
JFreeChart chart = new JFreeChart("Example", combinedDomainXYPlot);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.addChartMouseListener(this);
CrosshairOverlay crosshairOverlay = new CrosshairOverlay();
this.xCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
this.xCrosshair.setLabelVisible(false);
this.yCrosshair = new Crosshair(Double.NaN, Color.GRAY, new BasicStroke(0f));
this.yCrosshair.setLabelVisible(false);
crosshairOverlay.addDomainCrosshair(xCrosshair);
crosshairOverlay.addRangeCrosshair(yCrosshair);
//chartPanel.addOverlay(crosshairOverlay);
LegendTitle legend = chart.getLegend();
legend.setPosition(RectangleEdge.RIGHT);
legend.setBackgroundPaint(Color.BLACK);
legend.setItemPaint(Color.GRAY);
legend.setItemFont(new Font("Arial", 1, 9));
app.getContentPane().add(chartPanel);
app.pack();
app.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
app.setVisible(true);
}
private XYPlot createPlot(){
XYDataset data1 = createDataset();
XYItemRenderer renderer1 = new StandardXYItemRenderer();
NumberAxis rangeAxis1 = new NumberAxis("Range 1");
XYPlot myPlot = new XYPlot(data1,null,rangeAxis1,renderer1);
myPlot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
return myPlot;
}
private CombinedDomainXYPlot createCombinedXYPlot(){
CombinedDomainXYPlot combinedDomainXYPlot = new CombinedDomainXYPlot(new NumberAxis());
combinedDomainXYPlot.setGap(5);
combinedDomainXYPlot.add(myPlot1,11);
return combinedDomainXYPlot;
}
private XYDataset createDataset() {
// create dataset 2...
final XYSeries series2 = new XYSeries("Series 2");
series2.add(10.0, 16853.2);
series2.add(20.0, 19642.3);
series2.add(30.0, 18253.5);
series2.add(40.0, 15352.3);
series2.add(50.0, 13532.0);
series2.add(100.0, 12635.3);
series2.add(110.0, 13998.2);
series2.add(120.0, 11943.2);
series2.add(130.0, 16943.9);
series2.add(140.0, 17843.2);
series2.add(150.0, 16495.3);
series2.add(160.0, 17943.6);
series2.add(170.0, 18500.7);
series2.add(180.0, 19595.9);
return new XYSeriesCollection(series2);
}
@Override
public void chartMouseClicked(ChartMouseEvent event) {
// would be happy to get this called without exception
System.out.print("Test");
}
@Override
public void chartMouseMoved(ChartMouseEvent event) {
// would be happy to get this called without exception
System.out.print("Test");
}
}
异常:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at org.jfree.chart.panel.CrosshairOverlay.paintOverlay(CrosshairOverlay.java:257)
at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1658)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5217)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
at javax.swing.JComponent.paint(JComponent.java:1042)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:79)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:116)
at java.awt.Container.paint(Container.java:1975)
at java.awt.Window.paint(Window.java:3904)
at javax.swing.RepaintManager.run(RepaintManager.java:842)
...
我可以在声明中找到这一点(在 CrosshairOverlay.java)
double yy = yAxis.valueToJava2D(y, dataArea, yAxisEdge);
抛出 NullPointerException 因为 yAxis
为空。该变量在上面的语句中赋值:
ValueAxis yAxis = plot.getRangeAxis();
函数 RangeAxis() 来自 XYPlot.java:
public ValueAxis getRangeAxis(int index) {
ValueAxis result = this.rangeAxes.get(index);
if (result == null) {
Plot parent = getParent();
if (parent instanceof XYPlot) {
XYPlot xy = (XYPlot) parent;
result = xy.getRangeAxis(index);
}
}
return result;
}
这里的parent不是XYPlot的实例,而是null。 getParent()
函数的注释说:
Returns the parent plot (or
null
if this plot is not part of a combined plot).
在这一点上我不明白为什么它 returns null
(也许是因为我在 CombinedXYPlot 中并且它没有父级?)。谁能帮我解释为什么父情节为空,以及如何解决我的问题?
我找到了问题的答案。我不得不通过修改 paintOverlay
函数来编写自己的 CrosshairOverlay
。现在(仅)为添加到 CombinedXYPlot
我不得不向下转换为 CombinedXYPlot
来获得一个函数,该函数为我提供了 XYPlot 子图的列表。从那以后,我选择了第一个子图,让十字准线绘画用这个来计算。
@Override
public void paintOverlay(Graphics2D g2, ChartPanel chartPanel) {
Shape savedClip = g2.getClip();
Rectangle2D dataArea = chartPanel.getScreenDataArea();
g2.clip(dataArea);
JFreeChart chart = chartPanel.getChart();
XYPlot plot = (XYPlot) chart.getPlot();
ValueAxis xAxis = plot.getDomainAxis();
RectangleEdge xAxisEdge = plot.getDomainAxisEdge();
Iterator iterator = getDomainCrosshairs().iterator();
while (iterator.hasNext()) {
Crosshair ch = (Crosshair) iterator.next();
if (ch.isVisible()) {
double x = ch.getValue();
double xx = xAxis.valueToJava2D(x, dataArea, xAxisEdge);
if (plot.getOrientation() == PlotOrientation.VERTICAL) {
drawVerticalCrosshair(g2, dataArea, xx, ch);
}
else {
drawHorizontalCrosshair(g2, dataArea, xx, ch);
}
}
}
// get subplots
List<XYPlot> subplots = ((CombinedDomainXYPlot) plot).getSubplots();
//take yAxis from first plot
ValueAxis yAxis = subplots.get(0).getRangeAxis();
RectangleEdge yAxisEdge = plot.getRangeAxisEdge();
iterator = this.getRangeCrosshairs().iterator();
while (iterator.hasNext()) {
Crosshair ch = (Crosshair) iterator.next();
if (ch.isVisible()) {
double y = ch.getValue();
double yy = yAxis.valueToJava2D(y, dataArea, yAxisEdge);
if (plot.getOrientation() == PlotOrientation.VERTICAL) {
drawHorizontalCrosshair(g2, dataArea, yy, ch);
}
else {
drawVerticalCrosshair(g2, dataArea, yy, ch);
}
}
}
g2.setClip(savedClip);
}
该解决方案的问题是,如果我向 CombinedXYPlot
添加子图, 十字准线位置将按每个子图的大小移动 。
我必须找到一种方法来获取子图的大小(每个图应该相同)并将其添加到 yy
位置的计算中。
这个问题与我原来的问题没有任何关系,所以我会等待帮助或等待更好的答案一段时间,并将此 post 标记为解决方案。