Search code examples
javaswingconstructorinitializationpaintcomponent

JSwing - calling a subroutine from within a constructor doesn't intitialise an array (which it should be doing)


I've got a PieChart class which extends the JPanel component which I'm using to draw (would you believe it) a pie chart.

I've got a subroutine called createArcs() which creates the array of arcs used to draw the pie chart. These arcs which are being created must be accessed elsewhere in the class (for testing if the mouse is over them).

When I call createArcs() from within the paintComponent() subroutine everything works fine. This isn't exactly ideal since that means every time paintComponent() is called the arcs are needlessly recalculated.

When createArcs() is called from within the constructor the JPanel pops up but only the background is drawn. I have other graphing objects that I'm testing and they also fail to render anything other than their background (plain white). It also appears as though the array has been initialised as I can access the properties of the arcs in the array from within the paintComponent() sub, it just fails to draw them.

Below is the working code, I would like to call createArcs() from within the constructor rather than the paintComponent() sub. Thanks for any and all help :).

Apologies for any formatting errors, I'm new :^)

import java.awt.*;
import java.awt.geom.Arc2D;

import javax.swing.*;

public class PieChart extends JPanel {

public static final int xBuffer = 10;
public static final int yBuffer = 10;

public Arc2D arcs[];

private static final long serialVersionUID = 1L;

int y[];

public PieChart(int y[]) { //Constructor sets up the array
    this.y = y;
}

public void setBounds(int x, int y, int width) { //Overrides so that the panel can only ever be a square
    super.setBounds(x, y, width, width);
}

public int getSegment(int x, int y) {
    int ret = -1;
    for (int i = 0; i < arcs.length; i++) {
        if (arcs[i].contains(x, y)) {
            ret = i;
        }
    }
    return ret;
}

public void paintComponent(Graphics g) {

    //Arcs are calculated every time paintComponent is called :((
    createArcs();
    //Sets up background
    super.paintComponent(g);
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, getWidth(), getHeight());

    //Create an array same size as the int array

    Graphics2D g2 = (Graphics2D) g;

    g.setColor(Util.generateColor(true));

    //Draws the arcs for the pie chart
    g.setColor(Util.generateColor(true));
    for (int i = 0; i < arcs.length; i++) {
        g2.fill(arcs[i]);
        g.setColor(Util.generateColor(false));
    }

}

public void createArcs() {
    arcs = new Arc2D[y.length];
    float normalisedY[] = new float[y.length];

    int maxY = 0;
    int total = 0;

    //Find maximum element and total of the data
    for (int i = 0; i < y.length; i++) {
        if (maxY < y[i]) {
            maxY = y[i];
        }
        total += y[i];
    }

    //Normalise the Y values in degrees
    for (int i = 0; i < y.length; i++) {
        normalisedY[i] = 360 * (((float) y[i] / (float) total));
    }

    int degreesTravelled = 0;
    //Creates arcs in a circle
    for (int i = 0; i < normalisedY.length; i++) {
        Arc2D arc = new Arc2D.Double(0, 0, getWidth(), getHeight(), degreesTravelled, Math.round(normalisedY[i]), Arc2D.PIE);
        arcs[i] = arc;
        degreesTravelled += Math.round(normalisedY[i]);
    }
}

}

Here is the non-functioning code :

import java.awt.*;
import java.awt.geom.Arc2D;

import javax.swing.*;

public class PieChart extends JPanel {

public static final int xBuffer = 10;
public static final int yBuffer = 10;

public Arc2D arcs[];

private static final long serialVersionUID = 1L;

int y[];

public PieChart(int y[]) { //Constructor sets up the array
    this.y = y;
    createArcs();
}

public void setBounds(int x, int y, int width) { //Overrides so that the panel can only ever be a square
    super.setBounds(x, y, width, width);
}

public int getSegment(int x, int y) {
    int ret = -1;
    for (int i = 0; i < arcs.length; i++) {
        if (arcs[i].contains(x, y)) {
            ret = i;
        }
    }
    return ret;
}

public void paintComponent(Graphics g) {

    //Arcs are calculated every time paintComponent is called :((

    //Sets up background
    super.paintComponent(g);
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, getWidth(), getHeight());

    //Create an array same size as the int array

    Graphics2D g2 = (Graphics2D) g;

    g.setColor(Util.generateColor(true));

    //Draws the arcs for the pie chart
    g.setColor(Util.generateColor(true));
    for (int i = 0; i < arcs.length; i++) {
        g2.fill(arcs[i]);
        g.setColor(Util.generateColor(false));
    }

}

public void createArcs() {
    arcs = new Arc2D[y.length];
    float normalisedY[] = new float[y.length];

    int maxY = 0;
    int total = 0;

    //Find maximum element and total of the data
    for (int i = 0; i < y.length; i++) {
        if (maxY < y[i]) {
            maxY = y[i];
        }
        total += y[i];
    }

    //Normalise the Y values in degrees
    for (int i = 0; i < y.length; i++) {
        normalisedY[i] = 360 * (((float) y[i] / (float) total));
    }

    int degreesTravelled = 0;
    //Creates arcs in a circle
    for (int i = 0; i < normalisedY.length; i++) {
        Arc2D arc = new Arc2D.Double(0, 0, getWidth(), getHeight(), degreesTravelled, Math.round(normalisedY[i]), Arc2D.PIE);
        arcs[i] = arc;
        degreesTravelled += Math.round(normalisedY[i]);
    }
}

}


Solution

  • Arc2D arc = new Arc2D.Double(0, 0, getWidth(), getHeight(), degreesTravelled, Math.round(normalisedY[i]), Arc2D.PIE);
    

    It looks like you have logic based on the width/height of the panel.

    Obviously when the panel is created is won't have a size until the frame is made visible.

    The painting method should only paint the state of a component, not change the state.

    So, maybe you need to add a ComponentListener to the panel to listen for a change in panel size at which time you would initialise the values in the Array.