Download the JDK
Search the Tutorials
Hide the TOC
Trail: Creating a GUI with JFC/Swing
Lesson: Performing Custom Painting
Performing Custom Painting
Creating the Demo Application (Step 1)
Creating the Demo Application (Step 2)
Creating the Demo Application (Step 3)
Refining the Design
A Closer Look at the Paint Mechanism
Summary
Solving Common Painting Problems
Home Page > Creating a GUI with JFC/Swing > Performing Custom Painting
Refining the Design

> For demonstration purposes it makes sense to keep the painting logic entirely contained within the `MyPanel` class. But if your application will need to track multiple instances, one pattern that you could use is to factor that code out into a separate class so that each square can be treated as an individual object. This technique is common in 2D game programming and is sometimes referred to as "sprite animation."

> Click the Launch button to run SwingPaintDemo4 using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.

>     package painting;

>     import javax.swing.SwingUtilities;
>     import javax.swing.JFrame;
>     import javax.swing.JPanel;
>     import javax.swing.BorderFactory;
>     import java.awt.Color;
>     import java.awt.Dimension;
>     import java.awt.Graphics;
>     import java.awt.event.MouseEvent;
>     import java.awt.event.MouseListener;
>     import java.awt.event.MouseAdapter;
>     import java.awt.event.MouseMotionListener;
>     import java.awt.event.MouseMotionAdapter;

>     public class SwingPaintDemo4 {

>         public static void main(String[] args) {

>             SwingUtilities.invokeLater(new Runnable() {
>                 public void run() {
>                     createAndShowGUI();
>                 }
>             });
>         }

>         private static void createAndShowGUI() {
>             System.out.println("Created GUI on EDT? "+
>             SwingUtilities.isEventDispatchThread());
>             JFrame f = new JFrame("Swing Paint Demo");
>             f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
>             f.add(new MyPanel());
>             f.setSize(250,250);
>             f.setVisible(true);
>         }

>     }

>     class MyPanel extends JPanel {

>         RedSquare redSquare = new RedSquare();

>         public MyPanel() {

>             setBorder(BorderFactory.createLineBorder(Color.black));

>             addMouseListener(new MouseAdapter(){
>                 public void mousePressed(MouseEvent e){
>                     moveSquare(e.getX(),e.getY());
>                 }
>             });

>             addMouseMotionListener(new MouseAdapter(){
>                 public void mouseDragged(MouseEvent e){
>                     moveSquare(e.getX(),e.getY());
>                 }
>             });

>         }

>         private void moveSquare(int x, int y){

>             // Current square state, stored as final variables
>             // to avoid repeat invocations of the same methods.
>             final int CURR_X = redSquare.getX();
>             final int CURR_Y = redSquare.getY();
>             final int CURR_W = redSquare.getWidth();
>             final int CURR_H = redSquare.getHeight();
>             final int OFFSET = 1;

>             if ((CURR_X!=x) || (CURR_Y!=y)) {

>                 // The square is moving, repaint background
>                 // over the old square location.
>                 repaint(CURR_X,CURR_Y,CURR_W+OFFSET,CURR_H+OFFSET);

>                 // Update coordinates.
>                 redSquare.setX(x);
>                 redSquare.setY(y);

>                 // Repaint the square at the new location.
>                 repaint(redSquare.getX(), redSquare.getY(),
>                         redSquare.getWidth()+OFFSET,
>                         redSquare.getHeight()+OFFSET);
>             }
>         }

>         public Dimension getPreferredSize() {
>             return new Dimension(250,200);
>         }

>         public void paintComponent(Graphics g) {
>             super.paintComponent(g);
>             g.drawString("This is my custom Panel!",10,20);

>             redSquare.paintSquare(g);
>         }
>     }

>     class RedSquare{

>         private int xPos = 50;
>         private int yPos = 50;
>         private int width = 20;
>         private int height = 20;

>         public void setX(int xPos){
>             this.xPos = xPos;
>         }

>         public int getX(){
>             return xPos;
>         }

>         public void setY(int yPos){
>             this.yPos = yPos;
>         }

>         public int getY(){
>             return yPos;
>         }

>         public int getWidth(){
>             return width;
>         }

>         public int getHeight(){
>             return height;
>         }

>         public void paintSquare(Graphics g){
>             g.setColor(Color.RED);
>             g.fillRect(xPos,yPos,width,height);
>             g.setColor(Color.BLACK);
>             g.drawRect(xPos,yPos,width,height);
>         }
>     }

> In this particular implementation we have created a `RedSquare` class entirely from scratch. Another approach would be to resuse the functionlity of `java.awt.Rectangle` by making the `RedSquare` a subclass of it. Regardless of how `RedSquare` is implemented, the important point is that we have given the class a method that accepts a `Graphics` object, and that method is invoked from the panel's `paintComponent` method. This separation keeps your code clean because it essentially tells each red square to paint itself.

Previous page: Creating the Demo Application (Step 3)
Next page: A Closer Look at the Paint Mechanism