Creating custom components in Swing

A few people, interested in experimenting more with Swing, have wanted to know how they can draw directly into a panel - similar to how DrawableFrame works, where you can draw into the panel. The bad news is that you can't just use DrawableFrame. But it's not all that hard.

What I'm going to do is to simply show you a short program that I wrote that illustrates how to create your own component. What this program does is draw the following window.

The basic idea is that you need to extend the JPanel class and override its paintComponent() method. This method is automatically called every time the JPanel component needs to redraw itself.

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

private class Canvas extends JPanel {
    // This is a custom-written component, implemented
    // by extending JPanel. It displays a sequence of NUM_RECTS
    // rectangles in a white component.
    private static final int NUM_RECTS = 40;
    private static final int WIDTH = 200; // width of component
    private static final int HEIGHT = 200; // height of component
    private static final int SIZE = 30; // maximum size of rectangle

    // These two instance variables are for remembering information
    // about the rectangles inside the component
    private Rectangle[] rects = new Rectangle[NUM_RECTS];
    private Color[] colors = new Color[NUM_RECTS];

    public Canvas() {
        // this sets up the size of the panel, so it isn't just a
        // zero-size panel. It also sets up the background color.
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setBackground(Color.white);

        // create random rectangles
        Random random = new Random();
        for(int i = 0; i < rects.length; i++) {
            switch(random.nextInt(3)) {
            case 0: colors[i] = Color.blue; break;
            case 1: colors[i] = Color.red; break;
            case 2: colors[i] = Color.green; break;
            }

            rects[i] = new Rectangle(random.nextInt(WIDTH - SIZE),
                random.nextInt(HEIGHT - SIZE), random.nextInt(SIZE),
                random.nextInt(SIZE));
        }
    }

    public void paintComponent(Graphics g) {
        // We're overriding JPanel's paintComponent method
        // so that we actually draw something whenever
        // the component is repainted.
        //
        // Note that a JPanel doesn't actually remember its contents
        // at all! We have to re-compute the contents of the JPanel
        // *every time* paintComponent is called.
        super.paintComponent(g); // (JPanel's paintComponent clears
                        // the panel with the background color.
                        // It's important that we call it to make
                        // this happen.)

        for(int i = 0; i < rects.length; i++) {
            g.setColor(colors[i]);
            g.fillRect(rects[i].x, rects[i].y,
                rects[i].width, rects[i].height);
        }
    }
}

public class JPanelDemo extends JFrame {
    public JPanelDemo() {
        setTitle("Rectangles");
        getContentPane().add(new Canvas());
        pack();
        show();
    }

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

If the contents of the panel change, and so you want the panel to be redrawn, you should call repaint() on the JPanel. This will schedule JPanel to call paintComponent() when it gets the opportunity.

If you want to pick up events when the user clicks, you'll want to use JPanel's addMouseListener() method.

For more information about this topic, you should check out Sun's tutorial documentation. The relevant page is the following.

http://java.sun.com/docs/books/tutorial/uiswing/painting/overview.html