概述
(一)在折线图单机鼠标之后出现竖线,同步显示该竖线和折线交点的纵坐标值和横坐标值
效果图
实现
TimeSeriesChart.java
该类为双纵轴折线图工具类
package encontrol.tool;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.block.*;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.CompositeTitle;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.*;
import org.jfree.ui.*;
import encontrol.data.TestData;
import encontrol.entity.ChartData;
import encontrol.entity.PointInfoBean;
/**
* 双纵轴折线图
* @author qinxinhua
* 2021年3月26日10:03:33
*/
public class TimeSeriesChart{
//每多少分钟一个点
private int timeLen;
//一共多少个点
private int count;
//图表
private JFreeChart chart;
private List<ChartData> dataChart;
private ChartPanel chartPan;
//图例字体
private Font fontLabel = new Font("苹方字体", Font.PLAIN, 17);
private Font tip = new Font("苹方字体", Font.PLAIN, 15);
//数据集
private TimeSeriesCollection dataset;
//存储数据点的横纵坐标
private Map<Integer,Map<Integer,Double>> posValue;
//鼠标移动误差为前后1分钟 60000
private double deviation;
//今天0点0分0秒是第几毫秒
private long dayBegin;
private Map<Integer,PointInfoBean> tips;
private int xPox = 0;
private int yPoy = 0;
private String timeNow="00:00";
private SimpleDateFormat pomit = new SimpleDateFormat("HH:mm");
/**
* 构造函数
*/
public TimeSeriesChart(){
init();
}
/**
* 初始化方法
*/
public void init() {
dataChart = new ArrayList<ChartData>();
posValue = new HashMap<Integer,Map<Integer,Double>>();
tips = new HashMap<Integer,PointInfoBean>();
dataset = new TimeSeriesCollection( );
timeLen = 15;
deviation = timeLen*(60*1000);
count = 1440/timeLen;
SimpleDateFormat sdfOne = new SimpleDateFormat("yyyy-MM-dd");
String day = sdfOne.format(Calendar.getInstance().getTime()).toString();
try {
dayBegin = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(day+" 00:00").getTime();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 获取堆叠图panel
* @return
*/
@SuppressWarnings("serial")
public JPanel getChartPanel(List<ChartData> dataList) {
dataChart = dataList;
JPanel chartPanel = new JPanel(new BorderLayout());
chartPanel.setOpaque(false);
JFreeChart chartPa = createChart(dataList);
chartPan = new ChartPanel(chartPa, false) {
private boolean domainCrosshairState = false;
protected transient Line2D verticalTraceLine1 ;
//序列号
int index = 0;
@Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
super.mouseClicked(arg0);
domainCrosshairState = !domainCrosshairState;
}
@Override
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
super.mouseMoved(arg0);
if(domainCrosshairState) {
int xPos = arg0.getX();
int yPos = arg0.getY();
Point2D point2D = chartPan.translateScreenToJava2D(new Point(xPos, yPos));
XYPlot xyPlot = (XYPlot)chart.getPlot();
ChartRenderingInfo chartRenderingInfo = chartPan.getChartRenderingInfo();
Rectangle2D rectangle2D = chartRenderingInfo.getPlotInfo().getDataArea();
ValueAxis valueDAxis = xyPlot.getDomainAxis(0);
RectangleEdge xrectangleEdge = xyPlot.getDomainAxisEdge(0);
double xalue = valueDAxis.java2DToValue(point2D.getX(), rectangle2D, xrectangleEdge);
long xvalue = new Double(xalue).longValue();
// System.out.println("-------------"+xvalue);
timeNow = pomit.format(xvalue).toString();
index = (int)Math.round((xvalue-dayBegin)/deviation);
System.out.println(timeNow);
if(posValue!=null&&posValue.size()>1) {
for(Integer key : posValue.keySet()) {
if(!tips.containsKey(key)) {
PointInfoBean pos = new PointInfoBean();
pos.setColor(dataChart.get(key).getColor());
pos.setInfo(dataChart.get(key).getName()+":0.0");
tips.put(key, pos);
}else {
Map<Integer,Double> poss = posValue.get(key);
tips.get(key).setxPos(xPos);
tips.get(key).setyPos(yPos+(key*20));
tips.get(key).setInfo(dataChart.get(key).getName()+":"+poss.get(index));
}
}
}
xPox = xPos;
yPoy = yPos;
repaint();
}
}
@Override
public void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponent(g);
if(domainCrosshairState) {
Graphics2D g2 = (Graphics2D) g;
Rectangle2D dataArea = getScreenDataArea();
g2.setColor(Color.decode("#2215DD"));
if (this.verticalTraceLine1 != null) {
g2.draw(this.verticalTraceLine1);
this.verticalTraceLine1.setLine(xPox, (int) dataArea.getMinY(), xPox, (int) dataArea.getMaxY());
} else {
this.verticalTraceLine1 = new Line2D.Float(xPox, (int) dataArea.getMinY(), xPox,
(int) dataArea.getMaxY());
}
g2.draw(this.verticalTraceLine1);
g2.setColor(Color.RED);
g2.setFont(tip);
g2.drawString(timeNow, xPox+5, yPoy);
if(tips!=null&&tips.size()>0) {
for (Integer key : tips.keySet()) {
g2.setColor(Color.decode(tips.get(key).getColor()));
g2.setFont(tip);
g2.drawString(tips.get(key).getInfo(), tips.get(key).getxPos()+5, tips.get(key).getyPos()+20);
// System.out.println(tips.get(key).getInfo()+"--------"+tips.get(key).getxPos()+"----------"+tips.get(key).getyPos()+"---------------"+tips.get(key).getColor());
}
}
g2.setColor(Color.BLACK);
g2.setPaintMode();
g2.dispose();
}
}
};
chartPan.setOpaque(false);
chartPanel.add(chartPan,BorderLayout.CENTER);
//图例
JPanel legend = new JPanel(new FlowLayout());
legend.setOpaque(false);
if(dataList!=null&&dataList.size()>0) {
for(final ChartData lin : dataList) {
JPanel leng = new JPanel(new BorderLayout());
leng.setPreferredSize(new Dimension(130,40));
leng.setOpaque(false);
@SuppressWarnings("serial")
JPanel img = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.decode("#521853"));
g2.setStroke(new BasicStroke(2));
int fontSize = 14;
g2.setFont(new Font(Font.SERIF, Font.PLAIN, fontSize));
g2.setColor(Color.decode(lin.getColor()));
g2.drawRect(3,(int)(this.getHeight()/2-8), 10,10);
g2.fillRect(3,(int)(this.getHeight()/2-8), 10,10);
}
};
img.setOpaque(false);
JPanel label = new JPanel();
label.setOpaque(false);
JLabel len = new JLabel();
len.setText(lin.getName());
len.setFont(fontLabel);
len.setForeground(Color.decode(lin.getColor()));
label.add(len);
leng.add(img,BorderLayout.WEST);
leng.add(label,BorderLayout.CENTER);
legend.add(leng);
}
}
chartPanel.add(legend,BorderLayout.SOUTH);
return chartPanel;
}
/**
* 查询数据之后刷新图表
*/
public void reshChart(List<ChartData> dataList) {
if(dataList!=null&&dataList.size()>1) {
dataChart = dataList;
//处理数据
XYDataset chartData = createChartData(0,dataList.get(0));
XYDataset chartData1 = createChartData(1,dataList.get(1));
//数据存储
//刷新数据
XYPlot plot = (XYPlot) chart.getPlot();
plot.setDataset(0, chartData); //关键代码
plot.setDataset(1, chartData1); //关键代码
}
}
/**
* 获取折线图
* @return
*/
@SuppressWarnings("deprecation")
private JFreeChart createChart(List<ChartData> dataList) {
XYDataset xydataset = new TimeSeriesCollection();
XYDataset xydataset1 = new TimeSeriesCollection();
if(dataList!=null&&dataList.size()>1) {
xydataset = createChartData(0,dataList.get(0));
xydataset1 = createChartData(1,dataList.get(1));
//数据存储
}
// XYDataset xydataset = createChartData(dataList.get(0));
chart = ChartFactory.createTimeSeriesChart( "", "", "", xydataset, false, true, false);
XYPlot xyplot = (XYPlot)chart.getPlot();
//纵轴1
NumberAxis numberaxis = (NumberAxis)xyplot.getRangeAxis();
numberaxis.setAutoRangeIncludesZero(false);
XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer)xyplot.getRenderer();
// xylineandshaperenderer.setToolTipGenerator(StandardXYToolTipGenerator.getTimeSeriesInstance());
xylineandshaperenderer.setShapesVisible(false);
xylineandshaperenderer.setShapesFilled(true);
// xylineandshaperenderer.setItemLabelsVisible(true);;
xylineandshaperenderer.setBaseItemLabelsVisible(true);
// xylineandshaperenderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER));
// xylineandshaperenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator());
//纵轴2
NumberAxis numberaxis1 = new NumberAxis( "");
numberaxis1.setAutoRangeIncludesZero(false);
xyplot.setRangeAxis(1, numberaxis1); //关键代码
xyplot.setDataset(1, xydataset1); //关键代码
xyplot.mapDatasetToRangeAxis(1, 1);
XYLineAndShapeRenderer xylineandshaperenderer1 = new XYLineAndShapeRenderer(true, true);
xylineandshaperenderer1.setShapesVisible(false);
xylineandshaperenderer1.setBaseItemLabelsVisible(true);
// xylineandshaperenderer1.setToolTipGenerator(StandardXYToolTipGenerator.getTimeSeriesInstance());
xyplot.setRenderer(1, xylineandshaperenderer1);
if(dataList!=null&&dataList.size()>1) {
xylineandshaperenderer.setSeriesPaint(0, Color.decode(dataList.get(0).getColor()));
xylineandshaperenderer1.setSeriesPaint(0, Color.decode(dataList.get(1).getColor()));
}
/*LegendTitle legendtitle = new LegendTitle(xylineandshaperenderer);
LegendTitle legendtitle1 = new LegendTitle(xylineandshaperenderer1);
BlockContainer blockcontainer = new BlockContainer(new BorderArrangement());
blockcontainer.add(legendtitle, RectangleEdge.LEFT);
blockcontainer.add(legendtitle1, RectangleEdge.RIGHT);*/
BlockContainer blockcontainer = new BlockContainer(new BorderArrangement());
blockcontainer.add(new EmptyBlock(2000D, 0.0D));
CompositeTitle compositetitle = new CompositeTitle(blockcontainer);
compositetitle.setPosition(RectangleEdge.BOTTOM);
chart.addSubtitle(compositetitle);
//设置折线图样式
renderChart();
return chart;
}
/**
* 折线图样式
*/
public void renderChart() {
chart.setBorderVisible(false);
chart.setBackgroundPaint(null);
chart.setBorderPaint(Color.red);
chart.setBorderStroke(null);
XYPlot xyplot = (XYPlot)chart.getPlot();
xyplot.setRangeZeroBaselineVisible(true);
xyplot.getDomainAxis().setLowerMargin(0.0D);
xyplot.getDomainAxis().setUpperMargin(0.0D);
xyplot.setBackgroundAlpha(0);
xyplot.setOutlineVisible(false);//数据区黑色边框不可见
xyplot.getDomainAxis().setAxisLinePaint(Color.decode("#3CB5FC"));// X坐标轴颜色
xyplot.getDomainAxis().setTickLabelFont(fontLabel);//X轴坐标上数值du字体
// 设置网格横线颜色setDomainGridlinePaint
xyplot.setRangeGridlinePaint(Color.decode("#1D566E"));
xyplot.setRangeGridlineStroke(new BasicStroke(2));
xyplot.setDomainGridlinesVisible(false);
//获取x轴
xyplot.getDomainAxis().setTickLabelPaint(Color.decode("#00F4FC"));
xyplot.getDomainAxis().setTickMarksVisible(false);
xyplot.getDomainAxis().setAxisLinePaint(Color.decode("#1D566E"));//x轴颜色
xyplot.getDomainAxis().setAxisLineStroke(new BasicStroke(2));
// 取得纵轴
xyplot.getRangeAxis().setAxisLineVisible(false);
xyplot.getRangeAxis().setTickMarksVisible(false);
xyplot.getRangeAxis().setTickLabelPaint(Color.decode("#13BBBF"));
xyplot.getRangeAxis().setTickLabelFont(fontLabel);//X轴坐标上数值du字体
NumberAxis numberAxis = (NumberAxis) xyplot.getRangeAxis();
numberAxis.setUpperMargin(0.15);//设置最高数据显示与顶端的距离
numberAxis.setLowerMargin(2);//设置最低的一个值与图片底端的距离
numberAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits() );
numberAxis.setAutoRangeIncludesZero(false);
numberAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
xyplot.getRangeAxis(1).setAxisLineVisible(false);
xyplot.getRangeAxis(1).setTickMarksVisible(false);
xyplot.getRangeAxis(1).setTickLabelPaint(Color.decode("#13BBBF"));
xyplot.getRangeAxis(1).setTickLabelFont(fontLabel);//X轴坐标上数值du字体
NumberAxis numberAxis1 = (NumberAxis) xyplot.getRangeAxis(1);
numberAxis1.setUpperMargin(0.15);//设置最高数据显示与顶端的距离
numberAxis1.setLowerMargin(2);//设置最低的一个值与图片底端的距离
numberAxis1.setStandardTickUnits(NumberAxis.createIntegerTickUnits() );
numberAxis1.setAutoRangeIncludesZero(false);
numberAxis1.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
}
/**
* 折线图数据源
* @return
*/
private XYDataset createChartData(int con,ChartData dataList) {
TimeSeriesCollection dataset = new TimeSeriesCollection( );
TimeSeries chartData = new TimeSeries( dataList.getName(),Minute.class);
Map<Integer,Double> pos = new HashMap<Integer,Double>();
if(dataList!=null) {
long now = Calendar.getInstance().getTimeInMillis();
long overTime = 60;
long start;
try {
SimpleDateFormat sdfOne = new SimpleDateFormat("yyyy-MM-dd");
String day = sdfOne.format(Calendar.getInstance().getTime()).toString();
start = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(day+" 00:00").getTime();
overTime = (now - (sdfOne.parse(sdfOne.format(now)).getTime()))/(1000*60);//当前时间距离00:00:00的分钟数
for (int t = 0; t < count; t++) {
int num = t*timeLen;
if(num<overTime) {
Minute m = new Minute(new Date(start + timeLen*t*60*1000));
// pos.put(m.getFirstMillisecond(), dataList.getDataList().get(num).getYvalue());
pos.put(t, dataList.getDataList().get(num).getYvalue());
chartData.add(m, dataList.getDataList().get(num).getYvalue());
}else {
Minute m = new Minute(new Date(start + timeLen*t*60*1000));
// pos.put(m.getFirstMillisecond(), 0.0);
pos.put(t, 0.0);
chartData.add(m, null);
}
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
dataset.addSeries(chartData);
posValue.put(con, pos) ;
return dataset;
}
public static void main(String args[])
{
JFrame frame = new JFrame();
frame.setSize(new Dimension(640, 480));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
TimeSeriesChart a = new TimeSeriesChart();
frame.setContentPane(a.getChartPanel(TestData.getDateBsBx()));
frame.setVisible(true);
}
}
新建测试数据类TestData.java,新增方法getDateBsBx。
/**
* 坝上水位、坝下水位
*/
public static List<ChartData> getDateBsBx() {
List<ChartData> returndate = new ArrayList<ChartData>();
ChartData line4 = new ChartData();
line4.setName("购电费用");
line4.setColor("#4A7EBB");
List<ChartProperty> dateList4 = new ArrayList<ChartProperty>();
for(int i=0;i<1440;i++) {
ChartProperty d1 = new ChartProperty();
d1.setYvalue(Double.parseDouble(new Random().nextInt(200000)+""));
d1.setChartName("购电费用");
d1.setXvalue(""+i);
dateList4.add(d1);
}
line4.setDataList(dateList4);
returndate.add(line4);
ChartData line3 = new ChartData();
line3.setName("购电平均成本");
line3.setColor("#BE4B48");
List<ChartProperty> dateList3 = new ArrayList<ChartProperty>();
for(int i=0;i<1440;i++) {
ChartProperty d1 = new ChartProperty();
d1.setYvalue(Double.parseDouble(new Random().nextInt(500)+""));
d1.setChartName("购电平均成本");
d1.setXvalue(""+i);
dateList3.add(d1);
}
line3.setDataList(dateList3);
returndate.add(line3);
return returndate;
}
ChartData.java该类为图表数据实体类
package encontrol.entity;
import java.util.List;
/**
* 图表数据
* 2019年9月16日15:21:14
* @author Administrator
*
*/
public class ChartData {
private String id;//id
private String name;//图表中每类数据的名称
private String color;//颜色
private List<ChartProperty> dataList;//数据
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public List<ChartProperty> getDataList() {
return dataList;
}
public void setDataList(List<ChartProperty> dataList) {
this.dataList = dataList;
}
}
ChartProperty.java该类为折线每个点的xy数据实体类
package encontrol.entity;
/**
* 降雨实体
* 2019年9月16日15:21:23
*
* @author Administrator
*
*/
public class ChartProperty {
//纵坐标
private Double yvalue;
//横坐标
private String xvalue;
//图表名类型
private String chartName;
public Double getYvalue() {
return yvalue;
}
public void setYvalue(Double yvalue) {
this.yvalue = yvalue;
}
public String getXvalue() {
return xvalue;
}
public void setXvalue(String xvalue) {
this.xvalue = xvalue;
}
public String getChartName() {
return chartName;
}
public void setChartName(String chartName) {
this.chartName = chartName;
}
}
PointInfoBean.java 该类为记录鼠标坐标实体类
package encontrol.entity;
/**
* 绘制tips信息
* @author
* 2021年4月14日14:33:07
*/
public class PointInfoBean {
private int xPos;//绘制起点x坐标
private int yPos;//绘制起点y坐标
private String color;//绘制字符串颜色
private String info;//绘制字符串
public int getxPos() {
return xPos;
}
public void setxPos(int xPos) {
this.xPos = xPos;
}
public int getyPos() {
return yPos;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}