如何从JfreeChart获取[x,y]值以设置鼠标在屏幕上的位置? [英] How to get [x,y] values from a JfreeChart to set the mouse position on screen?

查看:45
本文介绍了如何从JfreeChart获取[x,y]值以设置鼠标在屏幕上的位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题是从先前的问题[

但是我希望它看起来如下:

为了获得第二张图像的效果[在交叉点处有价格提示],我需要模拟鼠标点动作,我知道如何使用机器人设置鼠标,但是我没有知道它的[x,y]值,所以我的问题是:如果我知道十字准线[x,y],如何将它们转换为鼠标的[x,y],所以当我将鼠标设置为那个位置,提示会显示出来吗?我试图在注释掉的代码[robot.mouseMove()]中实现这一点,但是它不起作用,正确的方法是什么?

解决方案

好的,感谢@trashgod,我想出了以下代码.

(1)它会在启动时显示信息,但是当用户移动鼠标后,它将消失并在新的鼠标位置显示信息:

  plot.getRenderer().removeAnnotation(note); 

(2)它也足够聪明,可以避免出现左右边缘,因此注释不会在图表的左侧和右侧被剪掉.

  note.setTextAnchor(Index< Volume_Color_Vector.size()/2?TextAnchor.TOP_LEFT:TextAnchor.TOP_RIGHT); 

完整代码:

  import java.awt.*;导入java.awt.event.*;导入java.text.*;导入java.util.*;导入javax.swing.*;导入org.jfree.chart.*;导入org.jfree.chart.annotations.*;导入org.jfree.chart.axis.*;导入org.jfree.chart.entity.*;导入org.jfree.chart.plot.XYPlot;导入org.jfree.chart.renderer.xy.XYBarRenderer;导入org.jfree.data.time.*;导入org.jfree.data.xy.XYDataset;导入org.jfree.chart.labels.*;导入org.jfree.chart.panel.*;导入org.jfree.chart.plot.*;导入org.jfree.chart.renderer.xy.*;导入org.jfree.chart.ui.*;公共类PriceVolume_Chart扩展了JPanel实现ChartMouseListener////价格图表的演示应用程序.{ChartPanel面板;TimeSeries Price_series =新的TimeSeries(价格");TimeSeries Volume_Series =新的TimeSeries(卷");十字准线x十字线,y十字线;静态Vector< String>Volume_Color_Vector = new Vector();XYTextAnnotation注释;XYPlot图;XYLineAndShapeRenderer r;公共PriceVolume_Chart(字符串符号,整数索引){尝试{JFreeChart chart = createChart(Symbol);panel = new ChartPanel(chart,true,true,true,false,true);panel.setPreferredSize(new java.awt.Dimension(1000,500));panel.addChartMouseListener(this);CrosshairOverlay crosshairOverlay =新的CrosshairOverlay();float [] dash = {2f,0f,2f};BasicStroke bs = new BasicStroke(1,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1.0f,dash,2f);xCrosshair =新的Crosshair(Double.NaN,Color.black,bs);xCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));xCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));xCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));xCrosshair.setLabelGenerator(new CrosshairLabelGenerator(){@Override公共字符串generateLabel(十字准线crosshair){long ms =(long)crosshair.getValue();TimeSeriesDataItem item = null;为(int i = 0; i< Volume_Series.getItemCount(); i ++){item = Volume_Series.getDataItem(i);如果(ms == item.getPeriod().getFirstMillisecond())中断;}long volume = item.getValue().longValue();返回NumberFormat.getInstance().format(volume);}});xCrosshair.setLabelVisible(true);yCrosshair =新的Crosshair(Double.NaN,Color.black,bs);yCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));yCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));yCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));yCrosshair.setLabelVisible(true);crosshairOverlay.addDomainCrosshair(xCrosshair);crosshairOverlay.addRangeCrosshair(yCrosshair);panel.addOverlay(crosshairOverlay);添加(面板);如果(Index!=-1&& Index< Volume_Series.getItemCount()){TimeSeriesDataItem itemX = Volume_Series.getDataItem(Index);xCrosshair.setValue(itemX.getPeriod().getFirstMillisecond());TimeSeriesDataItem itemY = Price_series.getDataItem(Index);yCrosshair.setValue(itemY.getValue().doubleValue());TimeSeriesDataItem item = Price_series.getDataItem(Index);double time = item.getPeriod().getFirstMillisecond();double price = item.getValue().doubleValue();SimpleDateFormat f =新的SimpleDateFormat("yyyy-MM-d");字符串st ="价格:"+ f.format(新日期((长)时间))+","+ NumberFormat.getCurrencyInstance().format(price)+";" ;;note =新的XYTextAnnotation(st,time,price-1);note.setFont(UIManager.getFont("ToolTip.font")));note.setBackgroundPaint(UIManager.getColor("ToolTip.background")));note.setTextAnchor(Index< Volume_Color_Vector.size()/2?TextAnchor.TOP_LEFT:TextAnchor.TOP_RIGHT);note.setOutlinePaint(Color.blue);note.setOutlineVisible(true);plot.getRenderer().addAnnotation(note);}}catch(Exception e){e.printStackTrace();}}私人JFreeChart createChart(String Symbol){createPriceDataset(Symbol);XYDataset priceData = new TimeSeriesCollection(Price_series);JFreeChart chart = ChartFactory.createTimeSeriesChart(Symbol,"Date",getYLabel("Price($)"),priceData,true,true,true);plot = chart.getXYPlot();plot.setBackgroundPaint(new Color(192,196,196));NumberAxis rangeAxis1 =(NumberAxis)plot.getRangeAxis();rangeAxis1.setLowerMargin(0.40);//为音量条留出空间plot.getRenderer().setDefaultToolTipGenerator(新的StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,new SimpleDateFormat("yyyy-MM-d"),NumberFormat.getCurrencyInstance()));NumberAxis rangeAxis2 = new NumberAxis("Volume");rangeAxis2.setUpperMargin(1.00);//为价格线留出空间rangeAxis2.setNumberFormatOverride(NumberFormat.getNumberInstance());plot.setRangeAxis(1,rangeAxis2);plot.setDataset(1,new TimeSeriesCollection(Volume_Series));plot.setRangeAxis(1,rangeAxis2);plot.mapDatasetToRangeAxis(1,1);MyRender Renderer = new MyRender();Renderer.setShadowVisible(false);plot.setRenderer(1,Renderer);DateAxis domainAxis =(DateAxis)plot.getDomainAxis();//考虑为对称性调整域轴的下边界.domainAxis.setLowerMargin(0.05);r =(XYLineAndShapeRenderer)plot.getRenderer();退货图表}私有无效createPriceDataset(字符串符号){String Lines [] = new String [21],Items [],Date;int年,月,日;长音量,Last_Volume = 0;双倍价格;第[0]行=日期,打开,高,低,关闭,调整关闭,成交量";第[1]行="2020-07-17,44.110001,44.369999,41.919998,42.509998,42.323395,849700";第[2]行="2020-07-20,41.630001,41.680000,39.669998,40.119999,39.943886,1319300&";第[3]行="2020-07-21,40.880001,42.860001,40.860001,42.270000,42.084450,2070300&";第[4]行="2020-07-22,41.919998,42.700001,41.090000,42.570000,42.383133,1317600&";第[5]行="2020-07-23,43.919998,46.389999,43.279999,44.759998,44.563519,1917700&";第[6]行="2020-07-24,46.500000,46.500000,43.950001,44.410000,44.215057,1384600&";第[7]行="2020-07-27,44.000000,44.240002,42.610001,43.860001,43.667469,799800";行[8] ="2020-07-28,43.389999,44.590000,42.930000,43.020000,42.831158,699700";第[9]行="2020-07-29,42.759998,45.590000,42.740002,45.430000,45.230579,826200";第[10]行="2020-07-30,44.160000,44.639999,42.959999,44.500000,44.304661,798100";第[11]行="2020-07-31,44.330002,44.419998,42.580002,44.360001,44.165276,1037800&";第[12]行="2020-08-03,44.560001,45.599998,43.419998,44.939999,44.742729,797000";行[13] ="2020-08-04,44.900002,45.500000,43.450001,43.540001,43.348877,971100";第[14]行="2020-08-05,44.860001,45.389999,43.650002,45.330002,45.131020,902000";第[15] ="2020-08-06,45.049999,46.279999,44.330002,45.299999,45.101147,645200";第[16]行="2020-08-07,44.849998,46.189999,44.189999,46.150002,45.947418,604900";第[17]行="2020-08-10,46.669998,48.410000,46.549999,47.290001,47.082417,960200";第[18]行="2020-08-11,49.110001,50.849998,48.799999,48.910000,48.695301,1187700&";第[19]行="2020-08-12,49.759998,50.009998,47.060001,47.840000,47.630001,752800'';第[20]行="2020-08-13,46.950001,48.369999,46.459999,47.110001,47.110001,535700'';为(int i = 1; i< Lines.length; i ++){Items = Lines [i] .split(,");Date = Items [0] .replace(-0",-");Price = Double.parseDouble(Items [5]);Volume = Long.parseLong(Items [6]);Items = Date.split(-");Year = Integer.parseInt(Items [0]);Month = Integer.parseInt(Items [1]);Day = Integer.parseInt(Items [2]);Price_series.add(新的Day(Day,Month,Year),Price);Volume_Series.add(new Day(Day,Month,Year),Volume);Volume_Color_Vector.add(Volume> = Last_Volume?"+":-");Last_Volume =音量;}}@Override公共无效chartMouseClicked(ChartMouseEvent事件){//忽略}公共无效chartMouseMoved(ChartMouseEvent cmevent){ChartEntity chartentity = cmevent.getEntity();如果(XYItemEntity的特权实例){如果(!r.getAnnotations().isEmpty())r.removeAnnotation(note);XYItemEntity e =(XYItemEntity)charityity;XYDataset d = e.getDataset();int s = e.getSeriesIndex();int i = e.getItem();双x = d.getXValue(s,i);双重y = d.getYValue(s,i);Out("x =" + x +" y ="+ y)";xCrosshair.setValue(x);yCrosshair.setValue(y);}}字符串getYLabel(字符串文本){字符串Result =";for(int i = 0; i< Text.length(); i ++)Result + = Text.charAt(i)+(i< Text.length()-1?//Out(结果);返回结果;}私有静态无效out(字符串消息){System.out.print(message);}私有静态无效Out(String message){System.out.println(message);}//创建并显示GUI.为了线程安全,应从事件调度线程中调用此方法.静态无效Create_And_Show_GUI(){final PriceVolume_Chart demo = new PriceVolume_Chart("ADS",9);JFrame frame = new JFrame("PriceVolume_Chart Frame");frame.add(演示);frame.addWindowListener(new WindowAdapter(){公共无效windowActivated(WindowEvent e){}公共无效windowClosed(WindowEvent e){}public void windowClosing(WindowEvent e){System.exit(0);}公共无效windowDeactivated(WindowEvent e){}public void windowDeiconified(WindowEvent e){demo.repaint();}public void windowGainedFocus(WindowEvent e){demo.repaint();}公共无效windowIconified(WindowEvent e){}公共无效windowLostFocus(WindowEvent e){}public void windowOpening(WindowEvent e){demo.repaint();}公共无效windowOpened(WindowEvent e){}public void windowResized(WindowEvent e){demo.repaint();}public void windowStateChanged(WindowEvent e){demo.repaint();}});frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);}公共静态void main(String [] args){//为事件调度线程安排作业:创建并显示此应用程序的GUI.SwingUtilities.invokeLater(new Runnable(){public void run(){Create_And_Show_GUI();}});}}MyRender类扩展XYBarRenderer{@Override公共Paint getItemPaint(int row,int col){this.setBarAlignmentFactor(0.5);//System.out.println(row +" + col +"+" super.getItemPaint(row,col)));返回PriceVolume_Chart.Volume_Color_Vector.elementAt(col).equals("+")?super.getItemPaint(row,col):new Color(0.56f,0.2f,0.5f,1f);}} 

This question evolved from a previous question [ In JFreeChart how to get the [x,y] values of a certain point on the chart? ], with a hint from @trashgod, I was able to reach the effect I was looking for, here is my current code :

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.entity.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.labels.*;
import org.jfree.chart.panel.*;
import org.jfree.chart.plot.*;

public class PriceVolume_Chart extends JPanel implements ChartMouseListener   // A demo application for price-volume chart.   
{
  ChartPanel panel;
  TimeSeries Price_series=new TimeSeries("Price");
  TimeSeries Volume_Series=new TimeSeries("Volume");
  Crosshair xCrosshair,yCrosshair;
  static Vector<String> Volume_Color_Vector=new Vector();
  Robot robot;

  public PriceVolume_Chart(String Symbol,int Index)
  {
    try
    {
      JFreeChart chart=createChart(Symbol);
      panel=new ChartPanel(chart,true,true,true,false,true);
      panel.setPreferredSize(new java.awt.Dimension(1000,500));
      panel.addChartMouseListener(this);
      CrosshairOverlay crosshairOverlay=new CrosshairOverlay();
      float[] dash={2f,0f,2f};
      BasicStroke bs=new BasicStroke(1,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1.0f,dash,2f);

      xCrosshair=new Crosshair(Double.NaN,Color.black,bs);
      xCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
      xCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
      xCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));

      xCrosshair.setLabelGenerator(new CrosshairLabelGenerator()
      {
        @Override
        public String generateLabel(Crosshair crosshair)
        {
          long ms=(long)crosshair.getValue();
          TimeSeriesDataItem item=null;
          for (int i=0;i<Volume_Series.getItemCount();i++)
          {
            item=Volume_Series.getDataItem(i);
            if (ms==item.getPeriod().getFirstMillisecond()) break;
          }
          long volume=item.getValue().longValue();
          return NumberFormat.getInstance().format(volume);
        }
      });

      xCrosshair.setLabelVisible(true);
      yCrosshair=new Crosshair(Double.NaN,Color.black,bs);
      yCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
      yCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
      yCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
      yCrosshair.setLabelVisible(true);
      crosshairOverlay.addDomainCrosshair(xCrosshair);
      crosshairOverlay.addRangeCrosshair(yCrosshair);
      panel.addOverlay(crosshairOverlay);
      add(panel);

      if (Index!=-1)
      {
        TimeSeriesDataItem itemX=Volume_Series.getDataItem(Index);
        xCrosshair.setValue(itemX.getPeriod().getFirstMillisecond());
        
        TimeSeriesDataItem itemY=Price_series.getDataItem(Index);
        yCrosshair.setValue(itemY.getValue().doubleValue());
/*
        robot=new Robot();
        robot.mouseMove((int)xCrosshair.getValue(),(int)yCrosshair.getValue());
        Out(" xCrosshair.getValue() = "+(int)xCrosshair.getValue()+"  yCrosshair.getValue() = "+(int)yCrosshair.getValue());
*/
      }
    }
    catch (Exception e) { e.printStackTrace(); }
  }

  private JFreeChart createChart(String Symbol)
  {
    createPriceDataset(Symbol);
    XYDataset priceData=new TimeSeriesCollection(Price_series);
    JFreeChart chart=ChartFactory.createTimeSeriesChart(Symbol,"Date",getYLabel("Price ( $ )"),priceData,true,true,true);
    XYPlot plot=chart.getXYPlot();
    plot.setBackgroundPaint(new Color(192,196,196));
    NumberAxis rangeAxis1=(NumberAxis)plot.getRangeAxis();
    rangeAxis1.setLowerMargin(0.40);                                           // Leave room for volume bars
    plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,new SimpleDateFormat("yyyy-MM-d"),NumberFormat.getCurrencyInstance()));

    NumberAxis rangeAxis2=new NumberAxis("Volume");
    rangeAxis2.setUpperMargin(1.00);                                           // Leave room for price line   
    rangeAxis2.setNumberFormatOverride(NumberFormat.getNumberInstance());
    plot.setRangeAxis(1,rangeAxis2);
    plot.setDataset(1,new TimeSeriesCollection(Volume_Series));
    plot.setRangeAxis(1,rangeAxis2);
    plot.mapDatasetToRangeAxis(1,1);
    MyRender Renderer=new MyRender();
    Renderer.setShadowVisible(false);
    plot.setRenderer(1,Renderer);

    DateAxis domainAxis=(DateAxis) plot.getDomainAxis();                     // Consider adjusting the lower margin of the domain axis for symmetry.
    domainAxis.setLowerMargin(0.05);

    return chart;
  }

  private void createPriceDataset(String Symbol)
  {
    String Lines[]=new String[21],Items[],Date;
    int Year, Month, Day;
    long Volume,Last_Volume=0;
    double Price;

    Lines[0]="Date,Open,High,Low,Close,Adj Close,Volume";
    Lines[1]="2020-07-17,44.110001,44.369999,41.919998,42.509998,42.323395,849700";
    Lines[2]="2020-07-20,41.630001,41.680000,39.669998,40.119999,39.943886,1319300";
    Lines[3]="2020-07-21,40.880001,42.860001,40.860001,42.270000,42.084450,2070300";
    Lines[4]="2020-07-22,41.919998,42.700001,41.090000,42.570000,42.383133,1317600";
    Lines[5]="2020-07-23,43.919998,46.389999,43.279999,44.759998,44.563519,1917700";
    Lines[6]="2020-07-24,46.500000,46.500000,43.950001,44.410000,44.215057,1384600";
    Lines[7]="2020-07-27,44.000000,44.240002,42.610001,43.860001,43.667469,799800";
    Lines[8]="2020-07-28,43.389999,44.590000,42.930000,43.020000,42.831158,699700";
    Lines[9]="2020-07-29,42.759998,45.590000,42.740002,45.430000,45.230579,826200";
    Lines[10]="2020-07-30,44.160000,44.639999,42.959999,44.500000,44.304661,798100";
    Lines[11]="2020-07-31,44.330002,44.419998,42.580002,44.360001,44.165276,1037800";
    Lines[12]="2020-08-03,44.560001,45.599998,43.419998,44.939999,44.742729,797000";
    Lines[13]="2020-08-04,44.900002,45.500000,43.450001,43.540001,43.348877,971100";
    Lines[14]="2020-08-05,44.860001,45.389999,43.650002,45.330002,45.131020,902000";
    Lines[15]="2020-08-06,45.049999,46.279999,44.330002,45.299999,45.101147,645200";
    Lines[16]="2020-08-07,44.849998,46.189999,44.189999,46.150002,45.947418,604900";
    Lines[17]="2020-08-10,46.669998,48.410000,46.549999,47.290001,47.082417,960200";
    Lines[18]="2020-08-11,49.110001,50.849998,48.799999,48.910000,48.695301,1187700";
    Lines[19]="2020-08-12,49.759998,50.009998,47.060001,47.840000,47.630001,752800";
    Lines[20]="2020-08-13,46.950001,48.369999,46.459999,47.110001,47.110001,535700";

    for (int i=1;i<Lines.length;i++)
    {
      Items=Lines[i].split(",");
      Date=Items[0].replace("-0","-");
      Price=Double.parseDouble(Items[5]);
      Volume=Long.parseLong(Items[6]);
      Items=Date.split("-");
      Year=Integer.parseInt(Items[0]);
      Month=Integer.parseInt(Items[1]);
      Day=Integer.parseInt(Items[2]);
      Price_series.add(new Day(Day,Month,Year),Price);
      Volume_Series.add(new Day(Day,Month,Year),Volume);
      Volume_Color_Vector.add(Volume>=Last_Volume?"+":"-");
      Last_Volume=Volume;
    }
  }

  @Override
  public void chartMouseClicked(ChartMouseEvent event)
  {
    // ignore
  }

  public void chartMouseMoved(ChartMouseEvent cmevent)
  {
    ChartEntity chartentity=cmevent.getEntity();
    if (chartentity instanceof XYItemEntity)
    {
      XYItemEntity e=(XYItemEntity)chartentity;
      XYDataset d=e.getDataset();
      int s=e.getSeriesIndex();
      int i=e.getItem();
      double x=d.getXValue(s,i);
      double y=d.getYValue(s,i);
      Out("x = "+x+"  y = "+y);
      xCrosshair.setValue(x);
      yCrosshair.setValue(y);
    }
  }

  String getYLabel(String Text)
  {
    String Result="";

    for (int i=0;i<Text.length();i++) Result+=Text.charAt(i)+(i<Text.length()-1?"\u2009":"");
//    Out(Result);
    return Result;
  }

  private static void out(String message) { System.out.print(message); }

  private static void Out(String message) { System.out.println(message); }

  // Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
  static void Create_And_Show_GUI()
  {
    final PriceVolume_Chart demo=new PriceVolume_Chart("ADS",9);

    JFrame frame=new JFrame("PriceVolume_Chart Frame");
    frame.add(demo);
    frame.addWindowListener(new WindowAdapter()
    {
      public void windowActivated(WindowEvent e) { }
      public void windowClosed(WindowEvent e) { }
      public void windowClosing(WindowEvent e) { System.exit(0); }
      public void windowDeactivated(WindowEvent e) { }
      public void windowDeiconified(WindowEvent e) { demo.repaint(); }
      public void windowGainedFocus(WindowEvent e) { demo.repaint(); }
      public void windowIconified(WindowEvent e) { }
      public void windowLostFocus(WindowEvent e) { }
      public void windowOpening(WindowEvent e) { demo.repaint(); }
      public void windowOpened(WindowEvent e) { }
      public void windowResized(WindowEvent e) { demo.repaint(); }
      public void windowStateChanged(WindowEvent e) { demo.repaint(); }
    });
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public static void main(String[] args)
  {
    // Schedule a job for the event-dispatching thread : creating and showing this application's GUI.
    SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } });
  }
}

class MyRender extends XYBarRenderer
{
  @Override
  public Paint getItemPaint(int row,int col)
  {
    this.setBarAlignmentFactor(0.5);
//    System.out.println(row+" "+col+" "+super.getItemPaint(row,col));
    return PriceVolume_Chart.Volume_Color_Vector.elementAt(col).equals("+")?super.getItemPaint(row,col):new Color(0.56f,0.2f,0.5f,1f);
  }
}

The app looks like this :

But I want it to look like the following :

In order to achieve the effect of the 2nd image [ with hint of the Price at the crossing point ], I need to simulate a mouse point action, I know how to set the mouse using a Robot, but I don't know the [x,y] values for that, so my question is : if I know the cross-hair [x,y], how to convert them to the [x,y] for the mouse so when I set the mouse to that position, the hint will show up ? I was trying to achieve that in the commented out code [ robot.mouseMove() ], but it didn't work, what's the right way to do that ?

解决方案

Alright, thanks to @trashgod, I came up with the following code.

(1) It would show info at start up, but after user moves mouse, it will disappear and show info at new mouse location:

plot.getRenderer().removeAnnotation(note);

(2) It's also smart enough to avoid left and right edge, so annotation won't be cut off at the left and right side of the chart.

note.setTextAnchor(Index<Volume_Color_Vector.size()/2
    ?TextAnchor.TOP_LEFT:TextAnchor.TOP_RIGHT);

Complete code:

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import org.jfree.chart.*;
import org.jfree.chart.annotations.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.entity.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.labels.*;
import org.jfree.chart.panel.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.ui.*;

public class PriceVolume_Chart extends JPanel implements ChartMouseListener   // A demo application for price-volume chart.   
{
  ChartPanel panel;
  TimeSeries Price_series=new TimeSeries("Price");
  TimeSeries Volume_Series=new TimeSeries("Volume");
  Crosshair xCrosshair,yCrosshair;
  static Vector<String> Volume_Color_Vector=new Vector();
  XYTextAnnotation note;
  XYPlot plot;
  XYLineAndShapeRenderer r;
  
  public PriceVolume_Chart(String Symbol,int Index)
  {
    try
    {
      JFreeChart chart=createChart(Symbol);
      panel=new ChartPanel(chart,true,true,true,false,true);
      panel.setPreferredSize(new java.awt.Dimension(1000,500));
      panel.addChartMouseListener(this);
      CrosshairOverlay crosshairOverlay=new CrosshairOverlay();
      float[] dash={2f,0f,2f};
      BasicStroke bs=new BasicStroke(1,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1.0f,dash,2f);

      xCrosshair=new Crosshair(Double.NaN,Color.black,bs);
      xCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
      xCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
      xCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));

      xCrosshair.setLabelGenerator(new CrosshairLabelGenerator()
      {
        @Override
        public String generateLabel(Crosshair crosshair)
        {
          long ms=(long)crosshair.getValue();
          TimeSeriesDataItem item=null;
          for (int i=0;i<Volume_Series.getItemCount();i++)
          {
            item=Volume_Series.getDataItem(i);
            if (ms==item.getPeriod().getFirstMillisecond()) break;
          }
          long volume=item.getValue().longValue();
          return NumberFormat.getInstance().format(volume);
        }
      });

      xCrosshair.setLabelVisible(true);
      yCrosshair=new Crosshair(Double.NaN,Color.black,bs);
      yCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
      yCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
      yCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
      yCrosshair.setLabelVisible(true);
      crosshairOverlay.addDomainCrosshair(xCrosshair);
      crosshairOverlay.addRangeCrosshair(yCrosshair);
      panel.addOverlay(crosshairOverlay);
      add(panel);

      if (Index!=-1 && Index<Volume_Series.getItemCount())
      {
        TimeSeriesDataItem itemX=Volume_Series.getDataItem(Index);
        xCrosshair.setValue(itemX.getPeriod().getFirstMillisecond());
        
        TimeSeriesDataItem itemY=Price_series.getDataItem(Index);
        yCrosshair.setValue(itemY.getValue().doubleValue());

        TimeSeriesDataItem item=Price_series.getDataItem(Index);
        double time=item.getPeriod().getFirstMillisecond();
        double price=item.getValue().doubleValue();
        SimpleDateFormat f=new SimpleDateFormat("yyyy-MM-d");
        String st=" Price : "+f.format(new Date((long)time))+" , "+NumberFormat.getCurrencyInstance().format(price)+" ";
        note=new XYTextAnnotation(st,time,price-1);
        note.setFont(UIManager.getFont("ToolTip.font"));
        note.setBackgroundPaint(UIManager.getColor("ToolTip.background"));
        note.setTextAnchor(Index<Volume_Color_Vector.size()/2?TextAnchor.TOP_LEFT:TextAnchor.TOP_RIGHT);
        note.setOutlinePaint(Color.blue);
        note.setOutlineVisible(true);
        plot.getRenderer().addAnnotation(note);
      }
    }
    catch (Exception e) { e.printStackTrace(); }
  }

  private JFreeChart createChart(String Symbol)
  {
    createPriceDataset(Symbol);
    XYDataset priceData=new TimeSeriesCollection(Price_series);
    JFreeChart chart=ChartFactory.createTimeSeriesChart(Symbol,"Date",getYLabel("Price ( $ )"),priceData,true,true,true);
    plot=chart.getXYPlot();
    plot.setBackgroundPaint(new Color(192,196,196));
    NumberAxis rangeAxis1=(NumberAxis)plot.getRangeAxis();
    rangeAxis1.setLowerMargin(0.40);                                           // Leave room for volume bars
    plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,new SimpleDateFormat("yyyy-MM-d"),NumberFormat.getCurrencyInstance()));

    NumberAxis rangeAxis2=new NumberAxis("Volume");
    rangeAxis2.setUpperMargin(1.00);                                           // Leave room for price line   
    rangeAxis2.setNumberFormatOverride(NumberFormat.getNumberInstance());
    plot.setRangeAxis(1,rangeAxis2);
    plot.setDataset(1,new TimeSeriesCollection(Volume_Series));
    plot.setRangeAxis(1,rangeAxis2);
    plot.mapDatasetToRangeAxis(1,1);
    MyRender Renderer=new MyRender();
    Renderer.setShadowVisible(false);
    plot.setRenderer(1,Renderer);

    DateAxis domainAxis=(DateAxis) plot.getDomainAxis();                     // Consider adjusting the lower margin of the domain axis for symmetry.
    domainAxis.setLowerMargin(0.05);
          
    r=(XYLineAndShapeRenderer)plot.getRenderer();

    return chart;
  }

  private void createPriceDataset(String Symbol)
  {
    String Lines[]=new String[21],Items[],Date;
    int Year, Month, Day;
    long Volume,Last_Volume=0;
    double Price;

    Lines[0]="Date,Open,High,Low,Close,Adj Close,Volume";
    Lines[1]="2020-07-17,44.110001,44.369999,41.919998,42.509998,42.323395,849700";
    Lines[2]="2020-07-20,41.630001,41.680000,39.669998,40.119999,39.943886,1319300";
    Lines[3]="2020-07-21,40.880001,42.860001,40.860001,42.270000,42.084450,2070300";
    Lines[4]="2020-07-22,41.919998,42.700001,41.090000,42.570000,42.383133,1317600";
    Lines[5]="2020-07-23,43.919998,46.389999,43.279999,44.759998,44.563519,1917700";
    Lines[6]="2020-07-24,46.500000,46.500000,43.950001,44.410000,44.215057,1384600";
    Lines[7]="2020-07-27,44.000000,44.240002,42.610001,43.860001,43.667469,799800";
    Lines[8]="2020-07-28,43.389999,44.590000,42.930000,43.020000,42.831158,699700";
    Lines[9]="2020-07-29,42.759998,45.590000,42.740002,45.430000,45.230579,826200";
    Lines[10]="2020-07-30,44.160000,44.639999,42.959999,44.500000,44.304661,798100";
    Lines[11]="2020-07-31,44.330002,44.419998,42.580002,44.360001,44.165276,1037800";
    Lines[12]="2020-08-03,44.560001,45.599998,43.419998,44.939999,44.742729,797000";
    Lines[13]="2020-08-04,44.900002,45.500000,43.450001,43.540001,43.348877,971100";
    Lines[14]="2020-08-05,44.860001,45.389999,43.650002,45.330002,45.131020,902000";
    Lines[15]="2020-08-06,45.049999,46.279999,44.330002,45.299999,45.101147,645200";
    Lines[16]="2020-08-07,44.849998,46.189999,44.189999,46.150002,45.947418,604900";
    Lines[17]="2020-08-10,46.669998,48.410000,46.549999,47.290001,47.082417,960200";
    Lines[18]="2020-08-11,49.110001,50.849998,48.799999,48.910000,48.695301,1187700";
    Lines[19]="2020-08-12,49.759998,50.009998,47.060001,47.840000,47.630001,752800";
    Lines[20]="2020-08-13,46.950001,48.369999,46.459999,47.110001,47.110001,535700";

    for (int i=1;i<Lines.length;i++)
    {
      Items=Lines[i].split(",");
      Date=Items[0].replace("-0","-");
      Price=Double.parseDouble(Items[5]);
      Volume=Long.parseLong(Items[6]);
      Items=Date.split("-");
      Year=Integer.parseInt(Items[0]);
      Month=Integer.parseInt(Items[1]);
      Day=Integer.parseInt(Items[2]);
      Price_series.add(new Day(Day,Month,Year),Price);
      Volume_Series.add(new Day(Day,Month,Year),Volume);
      Volume_Color_Vector.add(Volume>=Last_Volume?"+":"-");
      Last_Volume=Volume;
    }
  }

  @Override
  public void chartMouseClicked(ChartMouseEvent event)
  {
    // ignore
  }

  public void chartMouseMoved(ChartMouseEvent cmevent)
  {
    ChartEntity chartentity=cmevent.getEntity();
    if (chartentity instanceof XYItemEntity)
    {
      if (!r.getAnnotations().isEmpty()) r.removeAnnotation(note);

      XYItemEntity e=(XYItemEntity)chartentity;
      XYDataset d=e.getDataset();
      int s=e.getSeriesIndex();
      int i=e.getItem();
      double x=d.getXValue(s,i);
      double y=d.getYValue(s,i);
      Out("x = "+x+"  y = "+y);
      xCrosshair.setValue(x);
      yCrosshair.setValue(y);
    }
  }

  String getYLabel(String Text)
  {
    String Result="";

    for (int i=0;i<Text.length();i++) Result+=Text.charAt(i)+(i<Text.length()-1?"\u2009":"");
//    Out(Result);
    return Result;
  }

  private static void out(String message) { System.out.print(message); }

  private static void Out(String message) { System.out.println(message); }

  // Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
  static void Create_And_Show_GUI()
  {
    final PriceVolume_Chart demo=new PriceVolume_Chart("ADS",9);

    JFrame frame=new JFrame("PriceVolume_Chart Frame");
    frame.add(demo);
    frame.addWindowListener(new WindowAdapter()
    {
      public void windowActivated(WindowEvent e) { }
      public void windowClosed(WindowEvent e) { }
      public void windowClosing(WindowEvent e) { System.exit(0); }
      public void windowDeactivated(WindowEvent e) { }
      public void windowDeiconified(WindowEvent e) { demo.repaint(); }
      public void windowGainedFocus(WindowEvent e) { demo.repaint(); }
      public void windowIconified(WindowEvent e) { }
      public void windowLostFocus(WindowEvent e) { }
      public void windowOpening(WindowEvent e) { demo.repaint(); }
      public void windowOpened(WindowEvent e) { }
      public void windowResized(WindowEvent e) { demo.repaint(); }
      public void windowStateChanged(WindowEvent e) { demo.repaint(); }
    });
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public static void main(String[] args)
  {
    // Schedule a job for the event-dispatching thread : creating and showing this application's GUI.
    SwingUtilities.invokeLater(new Runnable() { public void run() { Create_And_Show_GUI(); } });
  }
}

class MyRender extends XYBarRenderer
{
  @Override
  public Paint getItemPaint(int row,int col)
  {
    this.setBarAlignmentFactor(0.5);
//    System.out.println(row+" "+col+" "+super.getItemPaint(row,col));
    return PriceVolume_Chart.Volume_Color_Vector.elementAt(col).equals("+")?super.getItemPaint(row,col):new Color(0.56f,0.2f,0.5f,1f);
  }
}

这篇关于如何从JfreeChart获取[x,y]值以设置鼠标在屏幕上的位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆