如何在域十字线中显示音量并以编程方式设置十字线? [英] How to display volume in the domain crosshair and set the crosshair programmatically?

查看:61
本文介绍了如何在域十字线中显示音量并以编程方式设置十字线?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题与我以前的问题有关[解决方案

如何正确显示十字线中的体积数据;现在显示的值不正确.

按照建议的此处," x 值是日期…缺少格式,十字准线正确显示距纪元的毫秒数."要更改十字准线的显示,请添加自定义> c0> .下面的实现在您的卷系列中搜索匹配的时间戳,并将相应的值格式化为数字.

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

考虑调整域轴的下边界以实现对称性.

DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
domainAxis.setLowerMargin(0.05);

还可以考虑将对齐因子设置为MyRenderer,以将条形图置于价格点的中心.

this.setBarAlignmentFactor(0.5);

当用户控制鼠标时,我将避免以编程方式移动十字准线.相反,请查看合适的AnnotationMarker.话虽如此,将您的chartMouseMoved()实现与此处所示的实现进行比较.您遇到的每个新XYItemEntity的更新,因此十字准线按"即可.到点或条.相比之下,后一个则通过java2DToValue()进行连续更新,如此处所述.

This question is related to my previous question [ How to show cross lines and red color for price drop in JFreeChart? ]. Thanks to @trashgod I have solved a lot of issues from that question, I'll ask what's left in this question. My sample code looks like this :

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.NumberAxis;
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();

  public PriceVolume_Chart(String Symbol)
  {
    JFreeChart chart=createChart(Symbol);
    panel=new ChartPanel(chart,true,true,true,false,true);
    panel.setPreferredSize(new 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.GRAY,bs);
    xCrosshair.setLabelBackgroundPaint(new Color(0f,0f,0f,1f));
    xCrosshair.setLabelFont(xCrosshair.getLabelFont().deriveFont(14f));
    xCrosshair.setLabelPaint(new Color(1f,1f,1f,1f));
    xCrosshair.setLabelVisible(true);
    yCrosshair=new Crosshair(Double.NaN,Color.GRAY,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);
    xCrosshair.setValue(1.5700752E12);
    xCrosshair.setVisible(true);
    yCrosshair.setValue(119.959007);
    yCrosshair.setVisible(true);
  }

  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,DateFormat.getDateInstance(), NumberFormat.getCurrencyInstance()));
    plot.getRenderer().setDefaultToolTipGenerator(new StandardXYToolTipGenerator(StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,new SimpleDateFormat("yyyy-MM-d"),NumberFormat.getCurrencyInstance()));
//    DecimalFormat format=new DecimalFormat("00.00");
//    rangeAxis1.setNumberFormatOverride(format);
//    rangeAxis1.setNumberFormatOverride(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);
    plot.setRenderer(1,new MyRender());

    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]="2019-09-23,129.589996,130.710007,128.240005,129.300003,126.555969,553700";
    Lines[2]="2019-09-24,129.309998,129.529999,125.500000,126.750000,124.060089,732900";
    Lines[3]="2019-09-25,126.570000,128.500000,126.190002,127.879997,125.166100,422000";
    Lines[4]="2019-09-26,127.849998,128.589996,127.169998,127.779999,125.068230,376100";
    Lines[5]="2019-09-27,128.669998,129.289993,126.389999,126.419998,123.737083,332900";
    Lines[6]="2019-09-30,126.589996,128.789993,125.849998,128.130005,125.410797,456700";
    Lines[7]="2019-10-01,129.039993,130.899994,125.480003,126.040001,123.365158,322700";
    Lines[8]="2019-10-02,125.059998,125.180000,121.620003,123.120003,120.507126,577100";
    Lines[9]="2019-10-03,122.650002,123.320000,119.089996,122.559998,119.959007,581300";
    Lines[10]="2019-10-04,122.970001,123.949997,121.320000,123.879997,121.250992,315700";
    Lines[11]="2019-10-07,123.139999,124.610001,122.669998,122.879997,120.272217,510300";
    Lines[12]="2019-10-08,121.720001,121.879997,118.089996,118.660004,116.141777,616600";
    Lines[13]="2019-10-09,119.410004,119.610001,116.680000,118.419998,115.906868,603300";
    Lines[14]="2019-10-10,119.089996,121.209999,117.080002,118.209999,115.701324,483300";
    Lines[15]="2019-10-11,120.330002,123.040001,119.720001,122.550003,119.949226,700500";
    Lines[16]="2019-10-14,122.550003,123.720001,120.940002,122.540001,119.939430,492900";
    Lines[17]="2019-10-15,122.849998,124.220001,121.230003,123.699997,121.074814,598200";
    Lines[18]="2019-10-16,123.889999,124.849998,122.800003,123.209999,120.595207,663600";
    Lines[19]="2019-10-17,123.449997,124.889999,122.790001,123.360001,120.742035,563200";
    Lines[20]="2019-10-18,123.050003,124.620003,122.459999,123.540001,120.918213,650300";

    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");

    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)
  {
    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);
  }
}

My questions are :

[1] How to correctly show Volume data in cross-hair [ it's now showing the incorrect value, yet I don't know how to fix it ] ?
[2] How to point the mouse on the N-th [ e.g. 12th ] day when the app starts ? 

More details to the 2nd question, when I move the mouse, I can output the x,y value of the mouse on the screen, and I can do the following to set the mouse at the desired location at start up :

...
panel.addOverlay(crosshairOverlay);
add(panel);
xCrosshair.setValue(1.5700752E12);
yCrosshair.setValue(119.959007);

But since I don't know the [x,y] location of the N-th day, therefore I don't know how to set the mouse on that day.

The reason I want to do this is : in my app, I want to look at some data on a certain day in another panel, and user can click on a certain day and I want the JFreeChart to instantly point to that day on the chart.

So my 2nd question is asking : "How can I get the [x,y] location of the N-th day on the chart ?

解决方案

How to correctly show volume data in cross-hair; it's now showing the incorrect value.

As suggested here, "The x value is the date…absent formatting, the crosshair correctly displays the number of milliseconds from the epoch." To alter the crosshair's display, add a custom CrosshairLabelGenerator. The implementation below searches your volume series for the matching timestamp and formats the corresponding value as a number.

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

Consider adjusting the lower margin of the domain axis for symmetry.

DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
domainAxis.setLowerMargin(0.05);

Also consider setting the alignment factor of MyRenderer to center the bar under the price point.

this.setBarAlignmentFactor(0.5);

As the user controls the mouse, I would avoid moving the crosshair programmatically. Instead, look at a suitable Annotation or Marker. Having said that, compare your implementation of chartMouseMoved() to the one shown here. Your's updates for each new XYItemEntity encountered, so the crosshairs "snap" to points or bars. In contrast, the latter one updates continuously, scaling via java2DToValue(), as described here.

这篇关于如何在域十字线中显示音量并以编程方式设置十字线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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