如何创建带时间轴的堆叠条形图?X轴分类渲染异常求助
解决JFreeChart水平堆叠时间条形图X轴渲染异常问题
我帮你梳理下代码里导致X轴(Category轴)异常的核心问题,以及对应的修正方案:
核心问题分析
数据集构建逻辑错误
- 代码里
createDataset方法存在无意义的嵌套循环(外层for (int i = 0; i < testData.size(); i++)完全多余),会导致数据被重复添加,造成图表渲染混乱。 - 堆叠条形图需要的是每个状态的持续时间长度,但你现在直接添加的是单个时间戳的值,这不符合堆叠图的逻辑,无法正确展示时间段。
- 代码里
轴与数据的适配问题
- 水平堆叠图中,Category轴(Domain轴)应该对应机器名称(比如
Mach-1),Date轴(Range轴)对应时间范围,但原代码没有将状态的持续时间映射为数值,导致轴渲染异常。
- 水平堆叠图中,Category轴(Domain轴)应该对应机器名称(比如
修正后的完整代码
import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.StackedBarRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.ui.RefineryUtilities; import javax.swing.*; import java.awt.*; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; public class StackedTimeBarChart extends JFrame { private static final String STANDBY_SERIES = "STANDBY"; private static final String HEATING_SERIES = "HEATING"; private static final String HOLDING_SERIES = "HOLDING"; private static final String COOLING_SERIES = "COOLING"; private static final String LOWERING_SERIES = "LOWERING"; private static final int STANDBY_SERIES_INDEX = 0; private static final int HEATING_SERIES_INDEX = 1; private static final int HOLDING_SERIES_INDEX = 2; private static final int COOLING_SERIES_INDEX = 3; private static final int LOWERING_SERIES_INDEX = 4; private static final Color STANDBY_COLOR = Color.DARK_GRAY; private static final Color HEATING_COLOR = Color.ORANGE; private static final Color HOLDING_COLOR = Color.YELLOW; private static final Color COOLING_COLOR = Color.CYAN; private static final Color LOWERING_COLOR = Color.GREEN; ArrayList<EventStatus> testData = null; CategoryPlot plot; private Long Datetolong(String date) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm"); Date inputDate = null; try { inputDate = simpleDateFormat.parse(date); } catch (ParseException e) { e.printStackTrace(); } return inputDate != null ? inputDate.getTime() : 0L; } public StackedTimeBarChart(String title) { super(title); // 初始化测试数据 initData(); // 创建图表核心组件 plot = new CategoryPlot(); plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); plot.setOrientation(PlotOrientation.HORIZONTAL); // 创建正确的数据集 CategoryDataset dataset = createDataset(); // 配置轴 CategoryAxis catAxis = new CategoryAxis("设备名称"); DateAxis dateAxis = new DateAxis("时间"); dateAxis.setVerticalTickLabels(true); dateAxis.setDateFormatOverride(new SimpleDateFormat("dd/MM/yyyy HH:mm")); // 配置渲染器 StackedBarRenderer rend = new StackedBarRenderer(); rend.setSeriesPaint(STANDBY_SERIES_INDEX, STANDBY_COLOR); rend.setSeriesPaint(HEATING_SERIES_INDEX, HEATING_COLOR); rend.setSeriesPaint(HOLDING_SERIES_INDEX, HOLDING_COLOR); rend.setSeriesPaint(COOLING_SERIES_INDEX, COOLING_COLOR); rend.setSeriesPaint(LOWERING_SERIES_INDEX, LOWERING_COLOR); // 组装图表 plot.setDataset(dataset); plot.setDomainAxis(catAxis); plot.setRangeAxis(dateAxis); plot.setRenderer(rend); JFreeChart chart = new JFreeChart("设备状态时间轴", JFreeChart.DEFAULT_TITLE_FONT, plot, true); ChartPanel chartPanel = new ChartPanel(chart); chartPanel.setPreferredSize(new Dimension(800, 450)); setContentPane(chartPanel); } private CategoryDataset createDataset() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); if (testData == null || testData.size() < 2) { plot.setNoDataMessage("NO DATA AVAILABLE"); return dataset; } // 计算每个状态的持续时间(从当前时间点到下一个时间点的间隔) for (int j = 0; j < testData.size() - 1; j++) { EventStatus current = testData.get(j); EventStatus next = testData.get(j + 1); long duration = next.getTime() - current.getTime(); // 持续时间(毫秒) String seriesName = getSeriesNameByStatus(current.getStatus()); // 将持续时间作为值添加,Category是设备名称 dataset.addValue(duration, seriesName, current.getName()); } // 处理最后一个状态(如果需要可以假设到当前时间,这里暂时忽略) return dataset; } private String getSeriesNameByStatus(int status) { return switch (status) { case STANDBY_SERIES_INDEX -> STANDBY_SERIES; case HEATING_SERIES_INDEX -> HEATING_SERIES; case HOLDING_SERIES_INDEX -> HOLDING_SERIES; case COOLING_SERIES_INDEX -> COOLING_SERIES; case LOWERING_SERIES_INDEX -> LOWERING_SERIES; default -> STANDBY_SERIES; }; } public static void main(String[] args) { StackedTimeBarChart demo = new StackedTimeBarChart("设备状态堆叠时间图"); demo.pack(); RefineryUtilities.centerFrameOnScreen(demo); demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); demo.setVisible(true); } private void initData() { testData = new ArrayList<EventStatus>(); testData.add(new EventStatus("Mach-1", Datetolong("03/04/2020 08:00"), 1)); testData.add(new EventStatus("Mach-1", Datetolong("03/04/2020 09:00"), 2)); testData.add(new EventStatus("Mach-1", Datetolong("03/04/2020 09:20"), 4)); testData.add(new EventStatus("Mach-1", Datetolong("03/04/2020 10:00"), 3)); } // 状态事件实体类 private class EventStatus { private String name; private long time; private int status; public EventStatus(String name, long time, int status) { this.name = name; this.time = time; this.status = status; } public String getName() { return name; } public long getTime() { return time; } public int getStatus() { return status; } }
关键修正点说明
- 移除冗余嵌套循环:删除了
createDataset中外层无意义的for (int i = 0; i < testData.size(); i++)循环,避免数据重复添加。 - 计算状态持续时间:改为计算相邻状态时间点的间隔作为堆叠值,这样堆叠条形图会正确展示每个状态的持续时间段。
- 优化轴的命名:给Category轴添加了“设备名称”的标签,让图表更易读。
- 完善空数据处理:增加了数据不足时的判断逻辑,避免空指针异常。
- 使用switch简化状态映射:用switch表达式替代冗长的if-else,代码更简洁。
内容的提问来源于stack exchange,提问作者ELIP
相关产品推荐
相关产品推荐

