1

我在使用 jFreeChart(1.0.14,最新的 maven 版本)时遇到了一个小布局问题。由于图像在这里比文字更重要:

样本输出

注意条形相对于子标签“A”、“B”、“C”、“D”的垂直位移。我不确定这是错误还是配置错误。请参阅下面附加的代码以进行复制。

此代码是从我当前的项目中取出的,因此其中可能有一些不必要的东西。

package de.test;

import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.swing.JFrame;
import javax.swing.JMenuBar;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryLabelPosition;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.CategoryLabelWidthType;
import org.jfree.chart.axis.SubCategoryAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.GroupedStackedBarRenderer;
import org.jfree.data.KeyToGroupMap;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.text.TextBlockAnchor;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.TextAnchor;

public class MainFrame extends JFrame {
    private static final long serialVersionUID = 5309704066433738856L;
    private ChartPanel panel;

    public static void main(String[] args) {
    new MainFrame();
    }

    public MainFrame() {
    JMenuBar menuBar = new JMenuBar();

    setDefaultCloseOperation(EXIT_ON_CLOSE);

    JFreeChart chart = ChartFactory.createStackedBarChart("", "", "", null, PlotOrientation.HORIZONTAL, false, true, false);
    chart.setBackgroundPaint(getBackground());
    chart.setTextAntiAlias(true);
    panel = new ChartPanel(chart, 400, 400, 100, 100, 1000, 1000, true, false, false, true, false, false);

    Set<String> subCategories = new TreeSet<>();
    GroupedStackedBarRenderer groupedStackedBarRenderer = new GroupedStackedBarRenderer();
    groupedStackedBarRenderer.setDrawBarOutline(true);

    GradientPaint billPaint = new GradientPaint(0.0F, 0.0F, Color.GREEN.darker(), 0.0F, 0.0F, Color.GREEN);
    GradientPaint creditPaint = new GradientPaint(0.0F, 0.0F, Color.RED.darker(), 0.0F, 0.0F, Color.RED);

    SubCategoryAxis subCategoryAxis = new SubCategoryAxis(null);
    subCategoryAxis.setCategoryLabelPositions(CategoryLabelPositions.replaceLeftPosition( //
        subCategoryAxis.getCategoryLabelPositions(), //
        new CategoryLabelPosition(RectangleAnchor.CENTER, TextBlockAnchor.BOTTOM_CENTER, TextAnchor.BOTTOM_CENTER, -Math.PI / 2,
            CategoryLabelWidthType.CATEGORY, 1f)));
    subCategoryAxis.setMinorTickMarksVisible(false);
    subCategoryAxis.setAxisLineVisible(false);
    subCategoryAxis.setTickLabelFont(subCategoryAxis.getLabelFont().deriveFont(Font.BOLD));
    KeyToGroupMap keyToGroupMap = new KeyToGroupMap();

    Map<String, Map<String, Map<String, Double>>> map = generateRandomSet(new HashSet<>(Arrays.asList("ab", "cd", "ef", "gh")),
        new HashSet<>(Arrays.asList("A", "B", "C", "D")));
    DefaultCategoryDataset dataset = new DefaultCategoryDataset();
    for (String employee : map.keySet()) {
        Map<String, Map<String, Double>> classMap = map.get(employee);
        for (String serviceClass : classMap.keySet()) {
        Map<String, Double> typeMap = classMap.get(serviceClass);
        subCategories.add(serviceClass.toString());
        Double billValue = typeMap.containsKey("RE") ? (Double) typeMap.get("RE") : 0D;
        Double creditNoteValue = typeMap.containsKey("GU") ? (Double) typeMap.get("GU") : 0D;
        Double effectiveValue = Math.max(0, billValue - creditNoteValue);
        dataset.addValue(effectiveValue, serviceClass + " (RE)", employee);
        dataset.addValue(creditNoteValue, serviceClass + " (GU)", employee);
        }
    }
    CategoryPlot categoryPlot = (CategoryPlot) panel.getChart().getPlot();
    categoryPlot.setDataset(dataset);
    categoryPlot.setDomainAxis(subCategoryAxis);
    categoryPlot.setRenderer(groupedStackedBarRenderer);

    int counter = 0;
    for (String subCategory : subCategories) {
        groupedStackedBarRenderer.setSeriesPaint(counter * 2, billPaint);
        groupedStackedBarRenderer.setSeriesPaint(counter++ * 2 + 1, creditPaint);
        keyToGroupMap.mapKeyToGroup(subCategory + " (RE)", subCategory);
        keyToGroupMap.mapKeyToGroup(subCategory + " (GU)", subCategory);
        subCategoryAxis.addSubCategory(subCategory);
    }
    groupedStackedBarRenderer.setSeriesToGroupMap(keyToGroupMap);

    setJMenuBar(menuBar);
    add(panel);

    setTitle("main.title");

    pack();
    setVisible(true);

    }

    private Map<String, Map<String, Map<String, Double>>> generateRandomSet(Set<String> emplNames, Set<String> subNames) {
    Random rnd = new Random();
    Map<String, Map<String, Map<String, Double>>> result = new TreeMap<>();
    for (String empl : emplNames) {
        Map<String, Map<String, Double>> emplData = new TreeMap<>();
        for (String sub : subNames) {
        Map<String, Double> subData = new TreeMap<>();
        subData.put("RE", rnd.nextDouble() * 1000 + 1000);
        subData.put("GU", rnd.nextDouble() * 1000);
        emplData.put(sub, subData);
        }
        result.put(empl, emplData);
    }
    return result;
    }
}
4

1 回答 1

1

从源码中new KeyToGroupMap()可以看出,如果不指定默认组,则使用"Default Group"默认组。而这个隐含的默认组将被计入getGroupCount()函数中。

GroupedStackedBarRenderer将使用组数计算条形宽度、边距等。可能是在绘制条形图时添加了一个隐式组。因此标签未对齐条形。

修复A创建时用作默认组KeyToGroupMapKeyToGroupMap keyToGroupMap = new KeyToGroupMap("A");

顺便说一句,我不确定这是否是错误。

/**
 * Creates a new map with a default group named 'Default Group'.
 */
public KeyToGroupMap() {
    this("Default Group");
}

/**
 * Creates a new map with the specified default group.
 *
 * @param defaultGroup  the default group (<code>null</code> not permitted).
 */
public KeyToGroupMap(Comparable defaultGroup) {
    if (defaultGroup == null) {
        throw new IllegalArgumentException("Null 'defaultGroup' argument.");
    }
    this.defaultGroup = defaultGroup;
    this.groups = new ArrayList();
    this.keyToGroupMap = new HashMap();
}

/**
 * Returns the number of groups in the map.
 *
 * @return The number of groups in the map.
 */
public int getGroupCount() {
    return this.groups.size() + 1;
}
于 2013-04-17T10:56:52.087 回答