Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in Java
Prev Contents / Index Next

Drawing

In a good GUI framework, drawing should be reasonably easy—and it is, in the Swing library. The problem with any drawing example is that the calculations that determine where things go are typically a lot more complicated that the calls to the drawing routines, and these calculations are often mixed together with the drawing calls, so it can seem that the interface is more complicated than it actually is.

For simplicity, consider the problem of representing data on the screen—here, the data will be provided by the built-in Math.sin( ) method, that produces a mathematical sine function. To make things a little more interesting, and to further demonstrate how easy it is to use Swing components, a slider will be placed at the bottom of the form to dynamically control the number of sine wave cycles that are displayed. In addition, if you resize the window, you’ll see that the sine wave refits itself to the new window size.

Although any JComponent may be painted and thus used as a canvas, if you just want a straightforward drawing surface, you will typically inherit from a JPanel. The only method you need to override is paintComponent( ), which is called whenever that component must be repainted (you normally don’t need to worry about this, because the decision is managed by Swing). When it is called, Swing passes a Graphics object to the method, and you can then use this object to draw or paint on the surface.

In the following example, all the intelligence concerning painting is in the SineDraw class; the SineWave class simply configures the program and the slider control. Inside SineDraw, the setCycles( ) method provides a hook to allow another object—the slider control, in this case—to control the number of cycles.

//: c14:SineWave.java
// Drawing with Swing, using a JSlider.
// <applet code=SineWave width=700 height=400></applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;

class SineDraw extends JPanel {
  private static final int SCALEFACTOR = 200;
  private int cycles;
  private int points;
  private double[] sines;
  private int[] pts;
  public SineDraw() { setCycles(5); }
  public void setCycles(int newCycles) {
    cycles = newCycles;
    points = SCALEFACTOR * cycles * 2;
    sines = new double[points];
    for(int i = 0; i < points; i++) {
      double radians = (Math.PI/SCALEFACTOR) * i;
      sines[i] = Math.sin(radians);
    }
    repaint();
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    int maxWidth = getWidth();
    double hstep = (double)maxWidth/(double)points;
    int maxHeight = getHeight();
    pts = new int[points];
    for(int i = 0; i < points; i++)
      pts[i] =
        (int)(sines[i] * maxHeight/2 * .95 + maxHeight/2);
    g.setColor(Color.RED);
    for(int i = 1; i < points; i++) {
      int x1 = (int)((i - 1) * hstep);
      int x2 = (int)(i * hstep);
      int y1 = pts[i-1];
      int y2 = pts[i];
      g.drawLine(x1, y1, x2, y2);
    }
  }
}

public class SineWave extends JApplet {
  private SineDraw sines = new SineDraw();
  private JSlider adjustCycles = new JSlider(1, 30, 5);
  public void init() {
    Container cp = getContentPane();
    cp.add(sines);
    adjustCycles.addChangeListener(new ChangeListener() {
      public void stateChanged(ChangeEvent e) {
        sines.setCycles(
          ((JSlider)e.getSource()).getValue());
      }
    });
    cp.add(BorderLayout.SOUTH, adjustCycles);
  }
  public static void main(String[] args) {
    Console.run(new SineWave(), 700, 400);
  }
} ///:~


All of the fields and arrays are used in the calculation of the sine wave points; cycles indicates the number of complete sine waves desired, points contains the total number of points that will be graphed, sines contains the sine function values, and pts contains the y-coordinates of the points that will be drawn on the JPanel. The setCycles( ) method creates the arrays according to the number of points needed and fills the sines array with numbers. By calling repaint( ) , setCycles( ) forces paintComponent( ) to be called so the rest of the calculation and redraw will take place.

The first thing you must do when you override paintComponent( ) is to call the base-class version of the method. Then you are free to do whatever you like; normally, this means using the Graphics methods that you can find in the documentation for java.awt.Graphics (in the JDK documentation from java.sun.com) to draw and paint pixels onto the JPanel. Here, you can see that almost all the code is involved in performing the calculations; the only two method calls that actually manipulate the screen are setColor( ) and drawLine( ). You will probably have a similar experience when creating your own program that displays graphical data; you’ll spend most of your time figuring out what it is you want to draw, but the actual drawing process will be quite simple.

When I created this program, the bulk of my time was spent in getting the sine wave to display. Once I did that, I thought it would be nice to be able to dynamically change the number of cycles. My programming experiences when trying to do such things in other languages made me a bit reluctant to try this, but it turned out to be the easiest part of the project. I created a JSlider (the arguments are the left-most value of the JSlider, the right-most value, and the starting value, respectively, but there are other constructors as well) and dropped it into the JApplet. Then I looked at the JDK documentation and noticed that the only listener was the addChangeListener, which was triggered whenever the slider was changed enough for it to produce a different value. The only method for this was the obviously named stateChanged( ), which provided a ChangeEvent object so that I could look backward to the source of the change and find the new value. By calling the sines object’s setCycles( ), the new value was incorporated and the JPanel redrawn.

In general, you will find that most of your Swing problems can be solved by following a similar process, and you’ll find that it’s generally quite simple, even if you haven’t used a particular component before.

If your problem is more complex, there are other more sophisticated alternatives for drawing, including third-party JavaBeans components and the Java 2D API. These solutions are beyond the scope of this book, but you should look them up if your drawing code becomes too onerous.
Thinking in Java
Prev Contents / Index Next


 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire