You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JFreeChart中实现两次鼠标选择显示双十字准线?

JFreeChart实现双十字准线方案

JFreeChart原生仅支持单条十字准线,要实现两次点击后显示两条独立十字准线,需要通过捕获鼠标点击事件保存坐标 + 自定义叠加绘制来实现,以下是具体思路和代码:

核心思路

  1. 记录两次鼠标点击对应的**域轴(X轴)和范围轴(Y轴)**数据点坐标
  2. 通过ChartPanel的Overlay叠加层,在图表绘制完成后额外绘制两条十字准线
  3. 处理多次点击的逻辑(比如超过两次时,将最早的十字准线替换为最新点击的位置)

代码实现

1. 十字准线位置存储类

用于保存两次点击的数据点坐标:

class CrosshairPositions {
    private Double domain1, range1;
    private Double domain2, range2;

    // 添加新的点击位置,超过两次时自动替换最早的记录
    public void addPosition(double domain, double range) {
        if (domain1 == null) {
            domain1 = domain;
            range1 = range;
        } else if (domain2 == null) {
            domain2 = domain;
            range2 = range;
        } else {
            // 重置:把第一条移到第二条的位置,新位置作为第二条
            domain1 = domain2;
            range1 = range2;
            domain2 = domain;
            range2 = range;
        }
    }

    // Getter方法
    public Double getDomain1() { return domain1; }
    public Double getRange1() { return range1; }
    public Double getDomain2() { return domain2; }
    public Double getRange2() { return range2; }
}

2. 捕获鼠标点击事件

通过ChartMouseListener监听图表点击,获取点击位置对应的真实数据值:

CrosshairPositions crosshairPositions = new CrosshairPositions();
ChartPanel chartPanel = new ChartPanel(yourChart); // 替换成你的JFreeChart实例

chartPanel.addChartMouseListener(new ChartMouseListener() {
    @Override
    public void chartMouseClicked(ChartMouseEvent event) {
        ChartEntity entity = event.getEntity();
        // 仅处理数据点的点击事件
        if (entity instanceof XYItemEntity) {
            XYItemEntity itemEntity = (XYItemEntity) entity;
            XYDataset dataset = itemEntity.getDataset();
            int seriesIdx = itemEntity.getSeriesIndex();
            int itemIdx = itemEntity.getItem();
            
            // 获取点击位置对应的域轴和范围轴数值
            double domainValue = dataset.getXValue(seriesIdx, itemIdx);
            double rangeValue = dataset.getYValue(seriesIdx, itemIdx);
            
            crosshairPositions.addPosition(domainValue, rangeValue);
            chartPanel.repaint(); // 触发重绘,更新十字准线
        }
    }

    @Override
    public void chartMouseMoved(ChartMouseEvent event) {
        // 无需处理鼠标移动事件
    }
});

3. 叠加绘制双十字准线

使用Overlay在图表上绘制两条自定义十字准线,支持区分样式(比如颜色、虚线):

chartPanel.addOverlay(new Overlay() {
    @Override
    public void paintOverlay(Graphics2D g2, ChartPanel panel) {
        XYPlot plot = (XYPlot) yourChart.getPlot();
        Rectangle2D dataArea = panel.getScreenDataArea();

        // 绘制第一条十字准线(红色虚线)
        if (crosshairPositions.getDomain1() != null && crosshairPositions.getRange1() != null) {
            drawCustomCrosshair(g2, plot, dataArea, 
                crosshairPositions.getDomain1(), crosshairPositions.getRange1(), 
                Color.RED, new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0f, new float[]{5f,5f}, 0f));
        }
        // 绘制第二条十字准线(蓝色实线)
        if (crosshairPositions.getDomain2() != null && crosshairPositions.getRange2() != null) {
            drawCustomCrosshair(g2, plot, dataArea, 
                crosshairPositions.getDomain2(), crosshairPositions.getRange2(), 
                Color.BLUE, new BasicStroke(1.5f));
        }
    }

    // 通用绘制十字准线方法
    private void drawCustomCrosshair(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
                                     double domainVal, double rangeVal, Color color, Stroke stroke) {
        // 将数据坐标转换为屏幕坐标
        ValueAxis domainAxis = plot.getDomainAxis();
        ValueAxis rangeAxis = plot.getRangeAxis();
        double screenX = domainAxis.valueToJava2D(domainVal, dataArea, plot.getDomainAxisEdge());
        double screenY = rangeAxis.valueToJava2D(rangeVal, dataArea, plot.getRangeAxisEdge());

        // 保存原有绘图状态,避免影响其他绘制
        Stroke originalStroke = g2.getStroke();
        Color originalColor = g2.getColor();

        // 设置当前十字线样式
        g2.setStroke(stroke);
        g2.setColor(color);

        // 绘制垂直十字线(贯穿整个数据区高度)
        g2.draw(new Line2D.Double(screenX, dataArea.getMinY(), screenX, dataArea.getMaxY()));
        // 绘制水平十字线(贯穿整个数据区宽度)
        g2.draw(new Line2D.Double(dataArea.getMinX(), screenY, dataArea.getMaxX(), screenY));

        // 恢复原有绘图状态
        g2.setStroke(originalStroke);
        g2.setColor(originalColor);
    }
});

4. 可选:关闭原生十字准线

如果不需要原生的单条十字准线,可关闭:

XYPlot plot = (XYPlot) yourChart.getPlot();
plot.setDomainCrosshairVisible(false);
plot.setRangeCrosshairVisible(false);

注意事项

  • 代码中yourChart需要替换为你自己创建的JFreeChart实例
  • 可根据需求调整十字线的颜色、粗细、虚线样式
  • 若需要支持更多十字准线,只需扩展CrosshairPositions的存储逻辑即可

内容的提问来源于stack exchange,提问作者mahesh bhagirath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 23:47:39