Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Chart your way to custom graph components

Learn to build a graph framework and custom graph components

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 2 of 4

   public Graph(String title, int min, int max) {
      this.title = title;
      this.min = min;
      this.max = max;
      items = new Vector();
   } // end constructor


The constructor takes the graph title and the range of values, and we create empty vector for the individual graph items.

   public void reshape(int x, int y, int width, int height) {
      super.reshape(x, y,width, height);
      fm = getFontMetrics(getFont());
      titleHeight = fm.getHeight();
      labelWidth = Math.max(fm.stringWidth(new Integer(min).toString()),
                   fm.stringWidth(new Integer(max).toString())) + 2;
      top = padding + titleHeight;
      bottom = size().height - padding;
      left = padding + labelWidth;
      right = size().width - padding;
   } // end reshape


Note: In JDK 1.1, the reshape method is replaced with public void setBounds(Rectangle r). See the API documentation for details.

We override the reshape method, which is inherited down the chain from the Component class. The reshape method is called when the component is resized and when it is laid out the first time. We use this method to collect measurements, so that they will always be updated if the component is resized. We get the font metrics for the current font and assign the titleHeight variable the maximum height of that font. We get the maximum width of the labels, testing to see which one is bigger and then using that one. The top, bottom, left, and right variables are calculated from the other variables and represent the borders of the center graph drawing region. We'll use these variables in the subclasses of Graph. Note that all of the measurements take into account a current size of the component so that redrawing will be correct at any size or aspect. If we used hard-coded values, the component could not be resized.

Next, we'll draw the framework for the graph.

   public void paint(Graphics g) {
      // draw the title
      fm = getFontMetrics(getFont());
      g.drawString(title, (size().width - fm.stringWidth(title))/2, top);
      // draw the max and min values
      g.drawString(new Integer(min).toString(), padding, bottom);
      g.drawString(new Integer(max).toString(), padding, top + titleHeight);
      // draw the vertical and horizontal lines
      g.drawLine(left, top, left, bottom);
      g.drawLine(left, bottom, right, bottom);
   } // end paint


The framework is drawn in the paint method. We draw the title and labels in their appropriate places. We draw a vertical line at the left border of the graph drawing region, and a horizontal line at the bottom border.

In this next snippet we set the preferred size for the component by overriding the preferredSize method. The preferredSize method is also inherited from the Component class. Components can specify a preferred size and a minimum size. I have chosen a preferred width of 300 and a preferred height of 200. The layout manager will call this method when it lays out the component.

   public Dimension preferredSize() {
      return(new Dimension(300, 200));
   }
} // end Graph


Note: In JDK 1.1, the preferredSize method is replaced with public Dimension getPreferredSize().

Next, we need a facility for adding and removing the items to be graphed.

   public void addItem(String name, int value, Color col) {
      items.addElement(new GraphItem(name, value, col));
   } // end addItem
   public void addItem(String name, int value) {
      items.addElement(new GraphItem(name, value, Color.black));
   } // end addItem
   public void removeItem(String name) {
      for (int i = 0; i < items.size(); i++) {
         if (((GraphItem)items.elementAt(i)).title.equals(name))
            items.removeElementAt(i);
      }
   } // end removeItem
} // end Graph


I've modeled the addItem and removeItem methods after similar methods in the Choice class, so the code will have a familiar feel. Notice that we use two addItem methods here; we need a way to add items with or without a color. When an item is added, a new GraphItem object is created and added to the items vector. When an item is removed, the first one in the vector with that name will be removed. The GraphItem class is very simple; here is the code:

import java.awt.*;
class GraphItem {
   String title;
   int value;
   Color color;
   public GraphItem(String title, int value, Color color) {
      this.title = title;
      this.value = value;
      this.color = color;
   } // end constructor
} // end GraphItem


The GraphItem class acts as a holder for the variables relating to graph items. I've included Color here in case it will be used in a subclass of Graph.

With this framework in place, we can create extensions to handle each type of graph. This strategy is quite convenient; we don't have to go to the trouble of measuring the pixels for the framework again, and we can create subclasses to focus on filling in the graph drawing region.

Building the bar chart

Now that we have a graphing framework, we can customize it by extending Graph and implementing custom drawing. We'll begin with a simple bar chart, which we can use just like any other component. A typical bar chart is illustrated below. We'll fill in the graph drawing region by overriding the paint method to call the superclass paint method (to draw the framework), then we'll perform the custom drawing needed for this type of graph.


A typical bar chart
import java.awt.*;
public class BarChart extends Graph {
   int position;
   int increment;
   public BarChart(String title, int min, int max) {
      super(title, min, max);
   } // end constructor


To space the items evenly, we keep an increment variable to indicate the amount we will shift to the right for each item. The position variable is the current position, and the increment value is added to it each time. The constructor simply takes in values for the super constructor (Graph), which we call explicitly.

Now we can get down to some actual drawing.

   public void paint(Graphics g) {
      super.paint(g);
      increment = (right - left)/(items.size());
      position = left;
      Color temp = g.getColor();
      for (int i = 0; i < items.size(); i++) {
         GraphItem item = (GraphItem)items.elementAt(i);
         int adjustedValue = bottom - (((item.value - min)*(bottom - top))
                                       /(max - min));
         g.drawString(item.title, position + (increment -
                      fm.stringWidth(item.title))/2, adjustedValue - 2);
         g.setColor(item.color);
         g.fillRect(position, adjustedValue, increment,
                    bottom - adjustedValue);
         position+=increment;
         g.setColor(temp);
      }
   } // end paint
} // end BarChart


Let's take a close look at what's happening here. In the paint method, we call the superclass paint method to draw the graph framework. We then find the increment by subtracting the right edge from the left edge, and then dividing the result by the number of items. This value is the distance between the left edges of the graph items. Because we want the graph to be resizable, we base these values on the current value of the left and right variables inherited from Graph. Recall that the left, right, top, and bottom values are the current actual pixel measurements of the graph drawing region taken in the reshape method of Graph, and therefore available for our use. If we did not base our measurements on these values, the graph would not be resizable.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comments (2)
Login
Forgot your account info?

Yes, it does work!By Anonymous on March 26, 2009, 11:20 amThe author may take this lesson out of context and assume that you can wrap his work with a simple JFrame yourself, but indeed it does work just fine. www.GoMotocross.com

Reply | Read entire comment

graph framework and custom graph componentsBy danjospehj82 on March 11, 2009, 9:49 pmDoes this code actually work I wonder??

Reply | Read entire comment

View all comments

Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • Previous Step by Step articles