Creating a Simple Bar Chart in Java - Reads Data a

2019-09-08 04:22发布

问题:

Im required to create a simple bar chart for one of my projects. This program requires an input of 1 to 9 integers that are greater than zero. The program is then required to display a very simple bar graph with the integers displayed above the bars. My professor did not explain this program very well to me and this is the first time working with graphics.

The program requires:

A class "SimpleBarChart" (which will extend Frame) with private variables Bar[] bars and int [] inputData among others (like private Graphics g, private int windowWid, windowHt, etc) and the following functions. It also uses an auxiliary class Bars as shown:

class Bar
{ public int height, width, value, nix, nwy;
  public Bar() {}
  public Bar(int height, int width, int value, int nix, int nwy)
  { this.height = height; etc }
}

Next a constructor SimpleBarChart(), which calls readData(), createBars(), and drawBars().

A function private void readData(), which reads inputData for the bar-chart. Use the code given below, which uses JOptionPane.

A function private void createBars() to create the bars array on assigning width, height, value (bars[i].value = inputData[i]), nix, and nwy of each bar. Requires 25 pixels of space on top and bottom of the display-window, 10 pixels between the bars, and has to allow bar-heights to be scaled to the form of inputData items.

Finally a function private void drawBars() to draw the bars, one at a time with a suitable sleep-time between bars, with two different colors. Requires the use of g.drawString("" +b.value, nix + b.width/2, nwy - 10) to label each bar in black b its value at 10 pixel above its top.

Ive been trying to figure this out all day and I'm lost. Any help would be greatly appreciated!

Heres what code I have so far:

package simplebarchart;

import java.awt.*;
import java.awt.event.*;
import java.awt.Toolkit.*;
import javax.swing.*;
import java.io.*;
import java.util.*;


public class SimpleBarchart extends JFrame
{
    private final int OUTER_MARGIN = 20;
    private static final Color BACKGROUND_COLOR = Color.white;
    private static final Color BAR_COLOR = Color.red; 
    private int SPACE_ON_LEFT_RIGHT;
    private Image fImageBuffer;
    private Insets fInsets;
    private Graphics g;
    private Bar[] bars;
    private static int SLEEP = 500;
    private int[] inputData;


class Bar
{
    private int height, value;
    public int width;
    public int nwx;
    public int nwy;
    public Color color;

    public Bar() {}
    public Bar(int height, int width, int value, int nwx, int nwy)
    {
        this.height = height;
        this.width = width; 
        this.value = value; 
        this.nwx = nwx;
        this.nwy = nwy;
    }

}


public SimpleBarchart(final int[] inputData)
{
    this.inputData = inputData;
    addWindowListener(new WindowCloser());
    fInsets = getInsets();
    setSize(WIDTH + fInsets.left + fInsets.right, HEIGHT + fInsets.top + fInsets.bottom);
    setTitle("Bar Chart");
    if (((fImageBuffer = createImage(WIDTH, HEIGHT)) == null) ||
            ((g = fImageBuffer.getGraphics()) == null))
            System.exit(1);
    readData();
    createBars();
    getContentPane().add(new SimpleBarchart(inputData), BorderLayout.CENTER);
    setVisible(true);
}

/**
 *
 * @param g
 */
protected void paintComponent(final Graphics g) {
    g.drawImage(fImageBuffer, fInsets.left, fInsets.top, null);
}

class WindowCloser extends WindowAdapter
{
    @Override
    public void windowClosing(WindowEvent e)
    {
        System.exit(0);
    }
}

private void readData()
{
    String[] inputItems = JOptionPane.showInputDialog("Enter 1 to 9 integers > 0").trim().split(" +");
    int numData = inputItems.length;
    inputData = new int[numData];

    for (int itemIndex = 0; itemIndex < inputItems.length; itemIndex++)
        inputData[itemIndex] = numData;


}

private void createBars()
{

//Im confused on how to create the bars for this program. 
//This function requires 25 pixels of space on top and bottom of the display-    window, 10 pixels between the bars, and has to allow bar-heights to be **scaled to the form of inputData items.** 

    Bar[] bars = new Bar[];
    int pixelBetweenBars = 25;
    int width = 800 + 2*OUTER_MARGIN;
    int height = 600 + 2*OUTER_MARGIN;

}

private void drawBars(final Graphics g)
{
            int OUTER_MARGIN = 20,
            WIDTH = 800 + 2 * OUTER_MARGIN,
            HEIGHT = 600 + 2 * OUTER_MARGIN;


    g.setColor(BACKGROUND_COLOR);
    g.fillRect(0, 0, WIDTH, HEIGHT);

    g.setColor(BAR_COLOR);
    final int barWidth = 20;
    for (int itemIndex = 0; itemIndex < inputData.length; itemIndex++) {
        final int x = OUTER_MARGIN + 25 * itemIndex;
        final int barHeight = 10 * inputData[itemIndex];
        final int y = barHeight;
        g.fillRect(x, y, barWidth, barHeight);
    }
}


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

回答1:

I agree with Uttesh Kumar that JFreeChart is an excellent chart library and it really pays off to invest time into learning it. But since you are doing this project to learn more about graphics, it is probably better to code the drawing yourself.

First a few general remarks:

  • the Bar class is defined two times and both implementations are not used yet;
  • the createImage([...], [...]).getGraphics() construction looks rather exotic;
  • the readData method reads a space separated list of numbers, allocates memory, but does not store the numbers (you could use the Bar class here);
  • the createBars method currently declares three local variables that are not used yet (and does nothing else).

As shown in the Performing Custom Painting tutorial that was already recommended by MadProgrammer, a common approach to custom painting is by subclassing the JPanel class and overriding the paintComponent method. Studying this short tutorial will get you up to speed pretty quickly, so I really agree with this recommendation!

In the readData method, you could add a few lines to store the numbers (and switch to using the Bar class later):

for (int itemIndex = 0; itemIndex < inputItems.length; itemIndex++)
    inputData[itemIndex] = [...get a number from the inputItems string here...];

After the call to readData in the constructor, you could add:

getContentPane().add(new SimpleBarPanel(inputData), BorderLayout.CENTER);
setVisible(true);

This uses a new SimpleBarPanel class that takes care of the custom painting. (The call to setVisible should go last if you want the SimpleBarPanel painting to be shown.)

The SimpleBarPanel class could look like this:

import java.awt.*;
import javax.swing.*;

public class SimpleBarPanel extends JPanel {
    private static final Color BACKGROUND_COLOR = Color.white;
    private static final Color BAR_COLOR = Color.red;

    private int[] inputData;

    public SimpleBarPanel(final int[] inputData) {
        this.inputData = inputData;
    }

    @Override
    protected void paintComponent(final Graphics g) {
        super.paintComponent(g);

        drawBars(g);
    }

    private void drawBars(final Graphics g) {
        int /*i,*/ OUTER_MARGIN = 20,
                WIDTH = 800 + 2 * OUTER_MARGIN,
                HEIGHT = 600 + 2 * OUTER_MARGIN;
                /*SPACE_BETWEEN_BARS = 10, SPACE_ON_TOP_BOTTOM = 25;*/

        g.setColor(BACKGROUND_COLOR);
        g.fillRect(0, 0, WIDTH, HEIGHT);

        g.setColor(BAR_COLOR);
        final int barWidth = 20;
        for (int itemIndex = 0; itemIndex < inputData.length; itemIndex++) {
            final int x = OUTER_MARGIN + 25 * itemIndex;
            final int barHeight = 10 * inputData[itemIndex];
            final int y = [...y is calculated using barHeight; the higher the bar, the lower y should be...];
            g.fillRect(x, y, barWidth, barHeight);
        }
    }
}

Good luck with your project.