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 or later). 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 reuse 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.