有没有办法在JFreeChart中将十字准线标签调整几个像素?

huangapple go评论67阅读模式
英文:

Is there a way to adjust cross-hair label by a few pixels in JFreeChart?

问题

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 {
  // ... (rest of the code)

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

  // ... (rest of the code)

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

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

  static void Create_And_Show_GUI() {
    final PriceVolume_Chart demo = new PriceVolume_Chart("ADS", 19);

    JFrame frame = new JFrame("PriceVolume_Chart Frame");
    frame.add(demo);
    frame.addWindowListener(new WindowAdapter() {
      // ... (rest of the code)
    });
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    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);
    return PriceVolume_Chart.Volume_Color_Vector.elementAt(col).equals("+") ? super.getItemPaint(row, col)
        : new Color(0.56f, 0.2f, 0.5f, 1f);
  }
}

Please note that I have only provided the code portions you requested for translation. If you have any further questions or need additional assistance, feel free to ask.

英文:

This question is related to my previous question [ https://stackoverflow.com/questions/64217855/how-to-get-x-y-values-from-a-jfreechart-to-set-the-mouse-position-on-screen ], the complete code is listed below.

The apps runs fine, but I noticed that the cross-hair label sometimes looks a bit too tight, left or right edge is too narrow, like in the following screenshots, the top one is how it looks now, but I'd prefer to add an extra one or 2 pixels to the left or right edge of the label, so it will look like the bottom image and feels more comfortable, I've modified the app a bit but now the price label will show the same value for every day, what did I do wrong ?

Also why sometimes there is an underline [ side line ] in the label, sometimes there isn't ?

有没有办法在JFreeChart中将十字准线标签调整几个像素?

有没有办法在JFreeChart中将十字准线标签调整几个像素?

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.setLabelGenerator(new CrosshairLabelGenerator()
{
@Override
public String generateLabel(Crosshair crosshair)
{
long ms=(long)crosshair.getValue();
TimeSeriesDataItem item=null;
for (int i=0;i<Price_Series.getItemCount();i++)
{
item=Price_Series.getDataItem(i);
if (ms==item.getPeriod().getFirstMillisecond()) break;
}
double price=item.getValue().doubleValue();
return " "+NumberFormat.getInstance().format(price)+" ";
}
});
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",19);
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);
}
}

答案1

得分: 1

是的,您的 yCrosshair 使用了自定义的 CrosshairLabelGenerator,通常会返回最后一个价格。幸运的是,StandardCrosshairLabelGenerator 允许您更方便地修改外观。在内部实现中,使用 labelTemplate 在调用 MessageFormat.format() 时进行处理;我已添加了文本和空格以突出效果:

yCrosshair.setLabelGenerator(new StandardCrosshairLabelGenerator(
    " 价格:{0} ", NumberFormat.getCurrencyInstance()));

您的自定义 xCrosshair 可以使用类似的方法:

xCrosshair.setLabelGenerator(new StandardCrosshairLabelGenerator() {
    @Override
    public String generateLabel(Crosshair crosshair) {
        
        long volume = item.getValue().longValue();
        String s = NumberFormat.getInstance().format(volume);
        return MessageFormat.format(" 成交量:{0} ", s);
    }
});

此外,根据此处的讨论,覆盖 getPreferredSize() 方法,并考虑使用不同的布局以获得更好的调整大小效果:

public class PriceVolume_Chart extends JPanel implements ChartMouseListener {
    
    public PriceVolume_Chart(String Symbol, int Index) {
        super(new GridLayout());
        
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(800, 500);
    }
    
}
英文:

>the price label will show the same value for every day

Yes, your yCrosshair has a custom CrosshairLabelGenerator that usually returns the last price. Fortunately, a StandardCrosshairLabelGenerator allows you to alter the appearance more conveniently. Internally, the implementation uses the labelTemplate in a call to MessageFormat.format(); I've added text and spaces to highlight the effect:

yCrosshair.setLabelGenerator(new StandardCrosshairLabelGenerator(
" Price: {0} ", NumberFormat.getCurrencyInstance()));

A similar approach can be used in your custom xCrosshair:

xCrosshair.setLabelGenerator(new StandardCrosshairLabelGenerator() {
@Override
public String generateLabel(Crosshair crosshair) {
…
long volume = item.getValue().longValue();
String s = NumberFormat.getInstance().format(volume);
return MessageFormat.format(" Volume: {0} ", s);
}
});

有没有办法在JFreeChart中将十字准线标签调整几个像素?

In addition, override getPreferredSize(), as discussed here, and consider using a different layout for better resizing:

public class PriceVolume_Chart extends JPanel implements ChartMouseListener {
…
public PriceVolume_Chart(String Symbol, int Index) {
super(new GridLayout());
…
}
@Override
public Dimension getPreferredSize() {
return new Dimension(800, 500);
}
…
}

huangapple
  • 本文由 发表于 2020年10月9日 00:40:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/64266993.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定