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

JFreeChart自定义标注/标签位置及解决线条遮挡问题

JFreeChart 图表复刻问题解决方案

目标效果与当前实现

目标图表:
目标图表

当前开发效果:
开发中图表

待解决问题

  • 如何在setLabelLocation()方法允许的边界外添加轴标签;
  • 如何在指定位置添加数值标签;
  • 最右侧的绿色竖线被虚线轮廓遮挡,添加右侧次坐标轴后仍处于底层的解决办法。

问题解决方案

1. 边界外添加轴标签

JFreeChart默认轴标签的位置受限于setLabelLocation()的可选值,若要超出边界,可直接创建独立的TextTitle作为轴标签,通过调整偏移量放置到目标位置:

// 添加Y轴标签到左侧边界外
TextTitle yAxisLabel = new TextTitle("Fr(mN)");
yAxisLabel.setFont(new Font("SansSerif", Font.PLAIN, 12));
yAxisLabel.setPaint(Color.BLUE);
yAxisLabel.setRotation(Math.PI / 2); // 垂直旋转
yAxisLabel.setHorizontalAlignment(HorizontalAlignment.CENTER);
yAxisLabel.setPosition(RectangleEdge.LEFT);
// 调整左偏移,让标签显示在轴外侧
yAxisLabel.setInsets(new RectangleInsets(0, -40, 0, 0));
chart.addSubtitle(yAxisLabel);

2. 指定位置添加数值标签

使用XYTextAnnotation可以在图表任意坐标位置添加文本标签,只需指定目标坐标和文本内容:

// 添加x=4竖线的数值标签
XYTextAnnotation xMarkerLabel = new XYTextAnnotation("4s", 4.2, 0);
xMarkerLabel.setFont(new Font("SansSerif", Font.PLAIN, 10));
xMarkerLabel.setPaint(Color.GREEN);
plot.addAnnotation(xMarkerLabel);

// 添加y=4横线的数值标签
XYTextAnnotation yMarkerLabel = new XYTextAnnotation("4mN", 9.5, 4);
yMarkerLabel.setFont(new Font("SansSerif", Font.PLAIN, 10));
yMarkerLabel.setPaint(Color.GREEN);
plot.addAnnotation(yMarkerLabel);

3. 解决绿色竖线被遮挡问题

ValueMarker会被plot的虚线轮廓遮挡,改用XYLineAnnotation绘制竖线即可,因为annotation会绘制在所有plot元素的最上层:

// 用XYLineAnnotation绘制绿色竖线,覆盖整个Y轴范围
XYLineAnnotation greenVerticalLine = new XYLineAnnotation(4, -3, 4, 6, new BasicStroke(1.5f), Color.GREEN);
plot.addAnnotation(greenVerticalLine);

修改后的完整代码

package javaapplication;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Stroke;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.AxisLabelLocation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.Layer;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

public class JavaApplication { 
    public static void main(String[] args){
        PlotWindow pw = new PlotWindow();
        pw.setVisible(true);
    }
}

public class PlotWindow extends JFrame
{
    final double t_init = 0;
    final double step = 0.1;
    final double t_fin = 10;
    int lengthValues;
    double [] yValues;
    double [] xValues;

    //params
    double Fmax = 5;
    double Tau = 3;
    double alpha = 3;
    double deltaX;
    
            
    public PlotWindow()
    {
      super("Test JFreechart"); 
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      this.setLocationRelativeTo(null);
      
      // x y values
      this.lengthValues = (int) ((this.t_fin-t_init)/this.step) + 1;
      this.setTimeValues();
      this.computeDeltaX();
      this.setForceValues();
      
      
      JPanel jp = createChartPanel();
      jp.setPreferredSize(new java.awt.Dimension(546, 447));
      this.add(jp, BorderLayout.CENTER);

      this.pack();
      this.setPreferredSize(new Dimension(546, 447));
    }
    
    private void setTimeValues()
    {
        this.xValues = new double[this.lengthValues];
        for(int i = 0; i < this.lengthValues; ++i)
        {
          this.xValues[i] = this.t_init + i*this.step;    
        }
    }
   
    private void computeDeltaX()
    {
        this.deltaX = Math.sqrt(-this.Tau*Math.log(this.alpha/(1+this.alpha)));
    }
    
    private void setForceValues()
    {
        this.yValues = new double[lengthValues];
        for(int i = 0; i < lengthValues; ++i)
        {
          double A = this.Fmax*(1+1/this.alpha);
          double B = 1-Math.exp(-Math.pow(this.xValues[i]-this.deltaX, 2)/this.Tau);
          this.yValues[i] = A*B-this.Fmax/this.alpha;      
        }
    }
    
    private XYSeriesCollection createDataset() 
    {
        boolean autoSort = false;
        boolean allowDuplicateXValues = false;
        XYSeriesCollection dataset = new XYSeriesCollection();
        XYSeries series1 = new XYSeries("",autoSort,allowDuplicateXValues);
        
        for(int i = 0; i < lengthValues; ++i)
        {
            series1.add(this.xValues[i], this.yValues[i]);
        }
        dataset.addSeries(series1);
        return dataset;
    }
    
    
    private JPanel createChartPanel()
    {
        String chartTitle = "Wetting balance curve";
        String xAxisLabel = "";
        String yAxisLabel = "";

        XYSeriesCollection dataset = createDataset();
                
        JFreeChart chart = ChartFactory.createXYLineChart(chartTitle,
                xAxisLabel, yAxisLabel, dataset,PlotOrientation.VERTICAL,false,true,false);

        XYPlot plot = chart.getXYPlot();
        
        
        //title
        TextTitle tt = new TextTitle();
        tt.setText("C:\\MENISCO ST60\\Mesures\\22-5912-100.PM1");
        tt.setPaint(Color.BLUE);
        tt.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 10 ) );
        chart.setTitle(tt);
        //empty subtitle (offset for the title)
        TextTitle tts = new TextTitle();
        tts.setText(" ");
        tts.setPaint(Color.BLUE);
        tts.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, 6 ) );
        chart.addSubtitle(tts);

        // norm subtitle
        TextTitle normtt = new TextTitle("    " + "Norme : J-STD-002E");
        normtt.setFont(new Font("SansSerif", Font.BOLD, 12));
        normtt.setPosition(RectangleEdge.BOTTOM);
        normtt.setPaint(Color.BLACK);
        normtt.setHorizontalAlignment(HorizontalAlignment.LEFT);
        chart.addSubtitle(normtt);
        
        // fmoy subtitle
        TextTitle fmoytt = new TextTitle("    " + "Force moyenne à 0.900 S: 0.25mN");
        fmoytt.setFont(new Font("SansSerif", Font.PLAIN, 10));
        fmoytt.setPosition(RectangleEdge.BOTTOM);
        fmoytt.setPaint(Color.BLUE);
        fmoytt.setHorizontalAlignment(HorizontalAlignment.LEFT);
        chart.addSubtitle(fmoytt);
        
        // 新增:添加Y轴标签到边界外
        TextTitle yAxisLabel = new TextTitle("Fr(mN)");
        yAxisLabel.setFont(new Font("SansSerif", Font.PLAIN, 12));
        yAxisLabel.setPaint(Color.BLUE);
        yAxisLabel.setRotation(Math.PI / 2);
        yAxisLabel.setHorizontalAlignment(HorizontalAlignment.CENTER);
        yAxisLabel.setPosition(RectangleEdge.LEFT);
        yAxisLabel.setInsets(new RectangleInsets(0, -40, 0, 0));
        chart.addSubtitle(yAxisLabel);
              
        // axis
        //domain axis
        plot.getDomainAxis().setLowerMargin(0.0);
        plot.getDomainAxis().setUpperMargin(0.0);
        NumberAxis domain = (NumberAxis) plot.getDomainAxis();
        NumberFormat formatterd = DecimalFormat.getInstance();
        formatterd.setMinimumFractionDigits(0);
        domain.setNumberFormatOverride(formatterd);
        domain.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domain.setTickMarksVisible(false);
        plot.getDomainAxis().setAxisLineVisible(false);
        
        //range axis
        plot.getRangeAxis().setLabelAngle(Math.PI/2);
        plot.getRangeAxis().setLabelLocation(AxisLabelLocation.HIGH_END);
        NumberAxis range = (NumberAxis) plot.getRangeAxis();
        NumberFormat formatter = DecimalFormat.getInstance(Locale.ENGLISH);
        formatter.setMinimumFractionDigits(2);
        range.setNumberFormatOverride(formatter);
        plot.getRangeAxis().setAxisLineStroke(new BasicStroke(1.5f));
        plot.getRangeAxis().setAxisLinePaint(Color.BLUE);
        plot.getRangeAxis().setTickMarksVisible(true);
        plot.getRangeAxis().setTickMarkPaint(Color.BLACK);
        plot.getRangeAxis().setTickMarkStroke(new BasicStroke(1.5f));
        float lg = plot.getRangeAxis().getTickMarkOutsideLength();
        plot.getRangeAxis().setTickMarkInsideLength(lg);
        plot.getRangeAxis().setRange(-3, 6);

        // background;gridline;outline 
        plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0));
        plot.setDomainGridlinePaint(Color.DARK_GRAY);
        plot.setRangeGridlinePaint(Color.DARK_GRAY);
        plot.setBackgroundPaint(Color.white);
        plot.setOutlineStroke(plot.getDomainGridlineStroke()); //dashed outline
        plot.setOutlinePaint(Color.DARK_GRAY);
        XYItemRenderer  renderer =  plot.getRenderer();
        renderer.setSeriesPaint(0, ChartColor.VERY_DARK_GREEN); // set green color to the xyline

        // vertical lines
        ValueMarker marker0 = new ValueMarker(0.2,Color.MAGENTA,new BasicStroke(1.5f));
        ValueMarker marker1 = new ValueMarker(1,Color.MAGENTA,new BasicStroke(1.5f));
        plot.addDomainMarker(marker0, Layer.FOREGROUND);
        plot.addDomainMarker(marker1,Layer.FOREGROUND);

        // 修改:用XYLineAnnotation绘制绿色竖线,避免被遮挡
        XYLineAnnotation greenVerticalLine = new XYLineAnnotation(4, -3, 4, 6, new BasicStroke(1.5f), Color.GREEN);
        plot.addAnnotation(greenVerticalLine);

        //horizontal lines
        XYLineAnnotation line = new XYLineAnnotation(0, 4, 10, 4, new BasicStroke(2.0f), Color.green);
        plot.addAnnotation(line);
        XYLineAnnotation line0 = new XYLineAnnotation(0, 0, 10, 0, new BasicStroke(1.0f), Color.BLUE);
        plot.addAnnotation(line0);
       
        //dashed horizontal line
        float[] dash = {10.0f, 3.0f, 3.0f};
        Stroke dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
        XYLineAnnotation line1 = new XYLineAnnotation(0, -1, 10, -1, dashed, Color.MAGENTA);
        plot.addAnnotation(line1);
        
        // 新增:添加指定位置的数值标签
        XYTextAnnotation xMarkerLabel = new XYTextAnnotation("4s", 4.2, 0);
        xMarkerLabel.setFont(new Font("SansSerif", Font.PLAIN, 10));
        xMarkerLabel.setPaint(Color.GREEN);
        plot.addAnnotation(xMarkerLabel);
        
        XYTextAnnotation yMarkerLabel = new XYTextAnnotation("4mN", 9.5, 4);
        yMarkerLabel.setFont(new Font("SansSerif", Font.PLAIN, 10));
        yMarkerLabel.setPaint(Color.GREEN);
        plot.addAnnotation(yMarkerLabel);
        
        //right side axis
        NumberAxis range2 = new NumberAxis(" ");
        range2.setAxisLinePaint(Color.GREEN);
        range2.setAxisLineStroke(new BasicStroke(1.5f));
        range2.setTickMarksVisible(false);
        range2.setTickLabelsVisible(false);
        plot.setRangeAxis(1, range2);
        plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
                
        return new ChartPanel(chart);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 01:20:32