0

I have a simple graph with multiple bars. How can I put some text (preferably with a JLabel) over each bar? Here is the code that draws the graph:

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (values == null || values.length == 0)
        return;
    double minValue = 0;
    double maxValue = 10000;
    for (int i = 0; i < values.length; i++) {
        if (minValue > values[i])
            minValue = values[i];
        if (maxValue < values[i])
            maxValue = values[i];
    }

    Dimension d = getSize();
    int clientWidth = d.width;
    int clientHeight = d.height;
    int barWidth = clientWidth / values.length;

    Font titleFont = new Font("SansSerif", Font.BOLD, 20);
    FontMetrics titleFontMetrics = g.getFontMetrics(titleFont);
    Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
    FontMetrics labelFontMetrics = g.getFontMetrics(labelFont);

    int titleWidth = titleFontMetrics.stringWidth(title);
    int y = titleFontMetrics.getAscent();
    int x = (clientWidth - titleWidth) / 2;
    g.setFont(titleFont);
    g.drawString(title, x, y);

    int top = titleFontMetrics.getHeight();
    int bottom = labelFontMetrics.getHeight();
    if (maxValue == minValue)
        return;
    double scale = (clientHeight - top - bottom) / (maxValue - minValue);
    y = clientHeight - labelFontMetrics.getDescent();
    g.setFont(labelFont);

    for (int i = 0; i < values.length; i++) {
        int valueX = i * barWidth + 1;
        int valueY = top;
        int height = (int) (values[i] * scale);
        if (values[i] >= 0)
            valueY += (int) ((maxValue - values[i]) * scale);
        else {
            valueY += (int) (maxValue * scale);
            height = -height;
        }

        g.setColor(Color.red);
        g.fillRect(valueX, valueY, barWidth - 2, height);
        g.setColor(Color.black);
        JLabel testLabel = new JLabel("Test");
        g.drawRect(valueX, valueY, barWidth - 2, height);
        int labelWidth = labelFontMetrics.stringWidth(names[i]);
        x = i * barWidth + (barWidth - labelWidth) / 2;

        g.drawString(names[i], x, y);
    }

}

I thought it should be in the for loop but that doesn't work. Thanks in advance!

E.J. Kok
  • 15
  • 5
  • 2
    First of all you don't add the JLabel to any container component which means it won't get painted, second if you are going to override paintComponent just use drawString – Jayfray Jan 31 '17 at 15:55
  • 1
    NEVER create or add components from within paintComponent as that method should be used for painting and painting only. It may be called many times, meaning you'd be creating and adding components multiple times unnecessarily. Why not instead create the text as simply text and draw it using the `Graphics#drawString(...)` method. – Hovercraft Full Of Eels Jan 31 '17 at 15:55
  • Despite its name, `paintComponent` is not supposed to deal with `Component` objects. You'd better paint custom labels with `drawRect`/`drawString` . – Arnaud Jan 31 '17 at 15:55
  • But the text that is going to be in the bar is live updated, more than once per second, will it still work with drawString? And can I put that in paintComponent? – E.J. Kok Jan 31 '17 at 15:58
  • `JLabel` itself probably uses `drawRect`/ `drawString` among other things, so don't worry, doing this yourself will be faster anyway. A frequency of once per second also shouldn't be a problem. – Arnaud Jan 31 '17 at 16:04
  • 2
    Yes the drawString goes in the paintComponent. You can force a paintComponent call by calling repaint on the component. Every time the text needs to change just call repaint – Jayfray Jan 31 '17 at 16:29

0 Answers0