Javascript required
Skip to content Skip to sidebar Skip to footer

Java Draw Circle With Color

This chapter shows y'all how yous can pigment your ain custom cartoon (such as graphs, charts, drawings and, in particular, calculator game avatars) considering you cannot detect standard GUI components that meets your requirements. I shall stress that you should endeavour to reuse the standard GUI components as far as possible and leave custom graphics equally the last resort. Nonetheless, custom graphics is crucial in game programming.

Read "Swing Tutorial" trail "Performing Custom Painting".

The java.awt.Graphics Class: Graphics Context and Custom Painting

A graphics context provides the capabilities of cartoon on the screen. The graphics context maintains states such as the color and font used in cartoon, as well as interacting with the underlying operating organization to perform the drawing. In Java, custom painting is done via the java.awt.Graphics grade, which manages a graphics context, and provides a set of device-independent methods for cartoon texts, figures and images on the screen on different platforms.

The java.awt.Graphics is an abstract class, as the actual act of cartoon is system-dependent and device-dependent. Each operating platform will provide a subclass of Graphics to perform the actual cartoon under the platform, but accommodate to the specification defined in Graphics.

AWT_GraphicsClassDiagram.png

Graphics Class' Cartoon Methods

The Graphics class provides methods for drawing three types of graphical objects:

  1. Text strings: via the drawString() method. Have annotation that Organisation.out.println() prints to the system console, not to the graphics screen.
  2. Vector-graphic primitives and shapes: via methods drawXxx() and fillXxx(), where 30 could be Line, Rect, Oval, Arc, PolyLine, RoundRect, or 3DRect.
  3. Bitmap images: via the drawImage() method.
            drawString(Cord          str, int          xBaselineLeft, int          yBaselineLeft);    drawLine(int          x1, int          y1, int          x2, int          y2); drawPolyline(int[]          xPoints, int[]          yPoints, int          numPoint);    drawRect(int          xTopLeft, int          yTopLeft, int          width, int          height); drawOval(int          xTopLeft, int          yTopLeft, int          width, int          top); drawArc(int          xTopLeft, int          yTopLeft, int          width, int          height, int          startAngle, int          arcAngle); draw3DRect(int          xTopLeft, int,          yTopLeft, int          width, int          peak, boolean          raised); drawRoundRect(int          xTopLeft, int          yTopLeft, int          width, int          elevation, int          arcWidth, int          arcHeight) drawPolygon(int[]          xPoints, int[]          yPoints, int          numPoint);    fillRect(int          xTopLeft, int          yTopLeft, int          width, int          meridian); fillOval(int          xTopLeft, int          yTopLeft, int          width, int          height); fillArc(int          xTopLeft, int          yTopLeft, int          width, int          height, int          startAngle, int          arcAngle); fill3DRect(int          xTopLeft, int,          yTopLeft, int          width, int          height, boolean          raised); fillRoundRect(int          xTopLeft, int          yTopLeft, int          width, int          meridian, int          arcWidth, int          arcHeight) fillPolygon(int[]          xPoints, int[]          yPoints, int          numPoint);    drawImage(Paradigm          img, int          xTopLeft, int          yTopLeft, ImageObserver          obs);   drawImage(Image          img, int          xTopLeft, int          yTopLeft, int          width, int          acme, ImageObserver          o);          

These drawing methods is illustrated beneath. The drawXxx() methods draw the outlines; while fillXxx() methods fill up the internal. Shapes with negative width and height volition not be painted. The drawImage() will be discussed later.

Graphics_drawXxx.png

Graphics Class' Methods for Maintaining the Graphics Context

The graphic context maintains states (or attributes) such every bit the current painting colour, the current font for drawing text strings, and the current painting rectangular area (called prune). You tin can use the methods getColor(), setColor(), getFont(), setFont(), getClipBounds(), setClip() to get or set up the colour, font, and clip surface area. Any painting outside the prune surface area is ignored.

            void setColor(Color          c) Color getColor()    void setFont(Font          f) Font getFont()   void setClip(int          xTopLeft, int          yTopLeft, int          width, int          height) void setClip(Shape          rect) public abstract void clipRect(int x, int y, int width, int height)  Rectangle getClipBounds()   Shape getClip()        

Graphics Class' Other Methods

void clearRect(int          x, int          y, int          width, int          height)     void copyArea(int          x, int          y, int          width, int          height, int          dx, int          dy)     void interpret(int          ten, int          y)     FontMetrics getFontMetrics() FontMetrics getFontMetrics(Font          f)        

Graphics Coordinate System

Graphics_Coordinates.png

In Java Windowing Subsystem (like most of the 2D Graphics systems), the origin (0,0) is located at the top-left corner.

EACH component/container has its own coordinate arrangement, ranging for (0,0) to (width-ane, acme-1) as illustrated.

You tin employ method getWidth() and getHeight() to call back the width and height of a component/container. Y'all tin utilise getX() or getY() to become the acme-left corner (ten,y) of this component's origin relative to its parent.

Custom Painting Template

Under Swing, custom painting is usually performed by extending (i.eastward., subclassing) a JPanel equally the cartoon sheet and override the paintComponent(Graphics g) method to perform your own drawing with the cartoon methods provided by the Graphics class. The Java Windowing Subsystem invokes (calls back) paintComponent(g) to return the JPanel past providing the current graphics context thou, which tin be used to invoke the cartoon methods. The extended JPanel is often programmed equally an inner class of a JFrame application to facilitate access of private variables/methods. Although nosotros typically draw on the JPanel, yous can in fact draw on any JComponent (such as JLabel, JButton).

The custom painting code template is as follows:

1 2 3 4 5 6 seven eight 9 x 11 12 13 xiv xv 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
import coffee.awt.*;        import java.awt.issue.*;  import javax.swing.*;         public class CGTemplate extends JFrame {       public static last int CANVAS_WIDTH  = 640;    public static terminal int CANVAS_HEIGHT = 480;         private DrawCanvas canvass;          public CGTemplate() {       canvas = new DrawCanvas();          canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));               Container cp = getContentPane();       cp.add(sail);               setDefaultCloseOperation(EXIT_ON_CLOSE);         pack();                    setTitle("......");        setVisible(truthful);       }                        individual class DrawCanvas extends JPanel {             @Override       public void paintComponent(Graphics g) {          super.paintComponent(g);              setBackground(Color.Blackness);                               grand.setColor(Colour.YELLOW);             1000.drawLine(thirty, 40, 100, 200);          g.drawOval(150, 180, 10, 10);          thousand.drawRect(200, 210, 20, xxx);          one thousand.setColor(Colour.RED);                chiliad.fillOval(300, 310, 30, 50);          yard.fillRect(400, 350, 60, 50);                   g.setColor(Colour.WHITE);          g.setFont(new Font("Monospaced", Font.PLAIN, 12));          chiliad.drawString("Testing custom drawing ...", 10, 20);       }    }                      public static void main(String[] args) {             SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new CGTemplate();          }       });    } }
Dissecting the Plan
  • Custom painting is performed by extending a JPanel (called DrawCanvas) and overrides the paintComponent(Graphics g) method to do your own drawing with the drawing methods provided past the Graphics course.
  • DrawCanvas is designed equally an inner form of this JFrame application, so as to facilitate admission of the individual variables/methods.
  • Coffee Windowing Subsystem invokes (calls back) paintComponent(g) to render the JPanel, with the current graphics context in g, whenever in that location is a need to refresh the display (e.g., during the initial launch, restore, resize, etc). You can utilise the cartoon methods (g.drawXxx() and m.fillXxx()) on the current graphics context g to perform custom painting on the JPanel.
  • The size of the JPanel is fix via the setPreferredSize(). The JFrame does non set its size, but packs the components contained via pack().
  • In the main(), the constructor is called in the event-dispatch thread via static method javax.swing.SwingUtilities.invokeLater() (instead of running in the master thread), to ensure thread-rubber and avoid deadlock, as recommended by the Swing developers.
(Avant-garde) Bearding Inner Class for Cartoon Canvas

Instead of a named-inner class called DrawCanvas in the previous instance, you tin also use an bearding inner course for the drawing canvas, if the painting code is short. For example,

            JPanel canvass = new JPanel() {    @Override    public void paintComponent(Graphics g) {       super.paintComponent(g);          
...... } }; ......
(Advanced) Getting the Graphics Context

You can retrieve the Graphics context of a JComponent via the getGraphics() method. This is, however, not unremarkably used. For example,

JPanel panel = new JPanel(); Graphics graphics = console.getGraphics();
Custom Painting in AWT (Obsolete)

Nether AWT, you can perform custom painting by extending coffee.awt.Canvass, and override the paint(Graphics one thousand) method, in a coffee.awt.Frame application. Similarly, you can explicitly invoke repaint() to update the graphics.

Refreshing the Display via repaint()

At times, we need to explicitly refresh the display (e.g., in game and animation). We shall NOT invoke paintComponent(Graphics) straight. Instead, nosotros invoke the JComponent's repaint() method. The Windowing Subsystem will in turn call dorsum the paintComponent() with the current Graphics context and execute it in the event-dispatching thread for thread prophylactic. You can repaint() a item JComponent (such as a JPanel) or the unabridged JFrame. The children contained within the JComponent will likewise be repainted.

Colors and Fonts

java.awt.Color

Graphics_StandardColors.png

The grade java.awt.Color provides 13 standard colors as named-constants. They are: Color.Blood-red, GREEN, BLUE, MAGENTA, CYAN, Xanthous, BLACK, WHITE, GRAY, DARK_GRAY, LIGHT_GRAY, Orange, and PINK. (In JDK 1.1, these constant names are in lowercase, eastward.k., red. This violates the Coffee naming convention for constants. In JDK i.ii, the uppercase names are added. The lowercase names were not removed for backward compatibility.)

You can employ the toString() to impress the RGB values of these color (e.g., System.out.println(Color.RED)):

Crimson       : java.awt.Color[r=255, g=0,   b=0] Light-green     : coffee.awt.Color[r=0,   g=255, b=0] Bluish      : java.awt.Color[r=0,   thousand=0,   b=255] Yellowish    : java.awt.Color[r=255, yard=255, b=0] MAGENTA   : java.awt.Color[r=255, g=0,   b=255] CYAN      : java.awt.Color[r=0,   one thousand=255, b=255] WHITE     : java.awt.Color[r=255, g=255, b=255] BLACK     : java.awt.Color[r=0,   g=0,   b=0] GRAY      : java.awt.Color[r=128, g=128, b=128] LIGHT_GRAY: coffee.awt.Colour[r=192, yard=192, b=192] DARK_GRAY : coffee.awt.Color[r=64,  g=64,  b=64] PINK      : coffee.awt.Colour[r=255, g=175, b=175] ORANGE    : java.awt.Colour[r=255, g=200, b=0]

Y'all can also use the RGB values or RGBA value (A for alpha to specify transparency/opaque) to construct your own color via constructors:

Color(int          r, int          g, int          b);              Colour(float          r, bladder          g, bladder          b);        Color(int          r, int          g, int          b, int          alpha);          Color(float          r, bladder          yard, float          b, float          alpha);         

For case:

Colour myColor1 = new Color(123, 111, 222); Color myColor2 = new Color(0.5f, 0.3f, 0.1f); Color myColor3 = new Colour(0.5f, 0.3f, 0.1f, 0.5f);        

To retrieve the individual components, you lot can use getRed(), getGreen(), getBlue(), getAlpha(), etc.

To set the groundwork and foreground (text) colour of a component/container, you tin can invoke:

JLabel label = new JLabel("Examination"); label.setBackground(Color.LIGHT_GRAY); label.setForeground(Color.Blood-red);

To set the color of the Graphics context thousand (for drawing lines, shapes, and texts), use chiliad.setColor(colour):

thousand.setColor(Color.RED); g.drawLine(10, 20, xxx, 40);    Colour myColor = new Color(123, 111, 222); g.setColor(myColor); g.drawRect(10, 10, 40, 50);           

(Advanced) JColorChooser Instance

Graphics_JColorChooser.png

This instance uses the javax.swing.JColorChooser to set the groundwork color of the JPanel.

ane 2 iii 4 v 6 7 eight 9 10 11 12 13 14 15 16 17 18 xix 20 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 fifty
import java.awt.*;         import java.awt.consequence.*;   import javax.swing.*;         @SuppressWarnings("serial") public class JColorChooserDemo extends JFrame {    JPanel console;    Color bgColor = Color.LIGHT_GRAY;            public JColorChooserDemo() {       console = new JPanel(new BorderLayout());         JButton btnColor = new JButton("Modify Colour");       console.add(btnColor, BorderLayout.SOUTH);       btnColor.addActionListener(new ActionListener() {          @Override          public void actionPerformed(ActionEvent evt) {             Color colour = JColorChooser.showDialog(JColorChooserDemo.this,                   "Cull a color", bgColor);             if (color != null) {                 bgColor = color;             }             panel.setBackground(bgColor);           }       });         setContentPane(console);         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       setTitle("JColorChooser Demo");       setSize(300, 200);       setLocationRelativeTo(null);         setVisible(true);                 }          public static void principal(String[] args) {              SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new JColorChooserDemo();            }       });    } }

java.awt.Font

The class java.awt.Font represents a specific font face, which can be used for rendering texts. You tin can use the following constructor to construct a Font case:

public Font(String          proper name, int          style, int          size);         

You can apply the setFont() method to set the current font for the Graphics context g for rendering texts. For example,

Font myFont1 = new Font(Font.MONOSPACED, Font.Patently, 12); Font myFont2 = new Font(Font.SERIF, Font.BOLD | Font.ITALIC, xvi);   JButton btn = new JButton("RESET"); btn.setFont(myFont1); JLabel lbl = new JLabel("How-do-you-do"); lbl.setFont(myFont2); ...... grand.drawString("In default Font", 10, 20);      Font myFont3 = new Font(Font.SANS_SERIF, Font.ITALIC, 12); g.setFont(myFont3); yard.drawString("Using the font set", 10, 50);          
Font'south Family Name vs. Font Name

A font could have many faces (or manner), e.thou., plain, bold or italic. All these faces take like typographic blueprint. The font confront name, or font name for curt, is the name of a particular font confront, like "Arial", "Arial Bold", "Arial Italic", "Arial Assuming Italic". The font family unit name is the proper name of the font family that determines the typographic design across several faces, like "Arial". For case,

java.awt.Font[family=Arial,name=Arial,style=manifestly,size=1] java.awt.Font[family=Arial,proper noun=Arial Assuming,style=plain,size=1] java.awt.Font[family=Arial,name=Arial Bold Italic,style=apparently,size=ane] java.awt.Font[family unit=Arial,name=Arial Italic,fashion=evidently,size=1]
Logical Font vs. Concrete Font

JDK supports these logical font family names: "Dialog", "DialogInput", "Monospaced", "Serif", or "SansSerif". JDK 1.6 provides these Cord constants: Font.DIALOG, Font.DIALOG_INPUT, Font.MONOSPACED, Font.SERIF, Font.SANS_SERIF.

Physical font names are bodily font libraries such as "Arial", "Times New Roman" in the arrangement.

GraphicsEnvironment'southward getAvailableFontFamilyNames() and getAllFonts()

Y'all can use GraphicsEnvironment'south getAvailableFontFamilyNames() to list all the font famiy names; and getAllFonts() to construct all Font instances (with font size of 1 pt). For example,

GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();         String[] fontNames = env.getAvailableFontFamilyNames(); for (String fontName : fontNames) {    Organisation.out.println(fontName); }         Font[] fonts = env.getAllFonts(); for (Font font : fonts) {    Arrangement.out.println(font); }
Font'south deriveFont()

You can utilize Font'due south deriveFont() to derive a new Font instance from this Font with varying size, style and others.

public Font deriveFont(float size) public Font deriveFont(int style) public Font deriveFont(AffineTransform trans) public Font deriveFont(int style, float size) public Font deriveFont(int fashion, AffineTransform trans)

For case,

Font font = new Font(Font.MONOSPACED, Font.Assuming, 12); System.out.println(font);     Font fontDerived = font.deriveFont(20);  Organization.out.println(fontDerived);        

(Avant-garde) java.awt.FontMetrics

The java.awt.FontMetrics class can exist used to measure the exact width and meridian of the string for a item font confront, and then that you lot can position the string equally you desire (such equally at the center of the screen).

To create a FontMetrics, use getFontMetrics() methods of the Graphics class, equally follows:

            public abstruse FontMetrics getFontMetrics(Font f)     public abstract FontMetrics getFontMetrics()            

Graphics_FontMetrics.png

            public int getHeight() public int getLeading() public int getAscent() public int getDescent()

The most usually-used part for FontMetrics is to measure the width of a given String displayed in a certain font.

public int stringWidth(String str)        

To centralize a cord on the cartoon canvas (eastward.thousand., JPanel):

public void paintComponent(Graphics 1000) {    super.paintComponent(g);    yard.setFont(new Font("Arial", Font.Bold, xxx));        FontMetrics fm = g.getFontMetrics();        Cord msg = "Howdy, world!";    int msgWidth = fm.stringWidth(msg);    int msgAscent = fm.getAscent();            int msgX = getWidth() / 2 - msgWidth / 2;    int msgY = getHeight() / 2 + msgAscent / ii;    m.drawString(msg, msgX, msgY); }

Custom Graphics Examples

Case 1: Moving an Object via Key/Button Activity

Graphics_MoveALine.png

This case illustrates how to re-paint the screen in response to a KeyEvent or ActionEvent.

The display consists of 2 JPanel in a JFrame, bundled in BorderLayout. The height panel is used for custom painting; the bottom panel holds two JButton bundled in FlowLayout. Clicking the "Movement Right" or "Motility Left" buttons moves the line. The JFrame listens to the "Left-arrow" and "Right-arrow" keys, and responses by moving the line left or correct.

one 2 three 4 five 6 7 8 9 10 eleven 12 13 fourteen fifteen 16 17 18 19 xx 21 22 23 24 25 26 27 28 29 xxx 31 32 33 34 35 36 37 38 39 xl 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 threescore 61 62 63 64 65 66 67 68 69 seventy 71 72 73 74 75 76 77 78 79 fourscore 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
import java.awt.*;        import java.awt.result.*;  import javax.swing.*;      @SuppressWarnings("serial") public class CGMoveALine extends JFrame {        public static final int CANVAS_WIDTH = 400;    public static concluding int CANVAS_HEIGHT = 140;    public static last Color LINE_COLOR = Color.BLACK;    public static terminal Color CANVAS_BACKGROUND = Colour.CYAN;          individual int x1 = CANVAS_WIDTH / two;    private int y1 = CANVAS_HEIGHT / 8;    private int x2 = x1;    private int y2 = CANVAS_HEIGHT / 8 * 7;      private DrawCanvas canvas;           public CGMoveALine() {              JPanel btnPanel = new JPanel(new FlowLayout());       JButton btnLeft = new JButton("Move Left ");       btnPanel.add(btnLeft);       btnLeft.addActionListener(new ActionListener() {          public void actionPerformed(ActionEvent evt) {             x1 -= 10;             x2 -= 10;             canvas.repaint();             requestFocus();           }       });       JButton btnRight = new JButton("Movement Right");       btnPanel.add(btnRight);       btnRight.addActionListener(new ActionListener() {          public void actionPerformed(ActionEvent evt) {             x1 += ten;             x2 += 10;             sail.repaint();             requestFocus();           }       });                sheet = new DrawCanvas();       canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));                Container cp = getContentPane();       cp.setLayout(new BorderLayout());       cp.add together(canvas, BorderLayout.Center);       cp.add(btnPanel, BorderLayout.S);                addKeyListener(new KeyAdapter() {          @Override          public void keyPressed(KeyEvent evt) {             switch(evt.getKeyCode()) {                instance KeyEvent.VK_LEFT:                   x1 -= x;                   x2 -= 10;                   repaint();                   interruption;                case KeyEvent.VK_RIGHT:                   x1 += 10;                   x2 += 10;                   repaint();                   break;             }          }       });         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        setTitle("Motility a Line");       pack();                  setVisible(true);        requestFocus();       }          class DrawCanvas extends JPanel {       @Override       public void paintComponent(Graphics chiliad) {          super.paintComponent(g);          setBackground(CANVAS_BACKGROUND);          g.setColor(LINE_COLOR);          g.drawLine(x1, y1, x2, y2);        }    }          public static void primary(String[] args) {              SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new CGMoveALine();           }       });    } }
Dissecting the Program
  • To practise custom painting, you have to make up one's mind which superclass to utilise. It is recommended that you apply a JPanel (or a more specialized Swing component such as JButton or JLabel). In this example, we extend the JPanel to do our custom painting, in an inner class, equally follows:
    class DrawCanvas extends JPanel {    @Override       public void paintComponent(Graphics g) {       super.paintComponent(chiliad);         setBackground(CANVAS_BACKGROUND);       grand.setColor(LINE_COLOR);       grand.drawLine(x1, y1, x2, y2);    } }
  • The paintComponent() method is overridden to provide the custom cartoon codes. We use the drawLine() method to depict a line from (x1,y1) to (x2, y2).
  • The paintComponent() method cannot exist called directly from your code, because it requires a Graphics object as argument.
  • paintComponent() is a and then-chosen "call-back" method. The Windowing subsystem invokes this method and provides a pre-configured Graphics object to correspond its state (e.thousand., current colour, font, clip expanse and etc). In that location are 2 kinds of painting: system-triggered painting and application-triggered painting. In a system-trigger painting, the system asking a component to render its content when the component is first made visible on the screen, or the component is resized, or the component is damaged that needs to exist repaint. In an awarding-triggered painting, the application invokes a repaint() request. Nether both cases, the Windowing subsystem volition call-back the paintComponent() to render the contents of the component with a proper Graphics object as argument.
  • In this instance, the application requests for a repaint() in the KeyEvent and MouseEvent handlers, which triggers the paintComponent() with an appropriate Graphics object as the argument.
  • To exist precise, when you invoke the repaint() method to repaint a JComponent, the Windowing subsystem calls-dorsum paint() method. The paint() method and so calls-back three methods: paintComponent(), paintBorder() and paintChilden().
  • In the overridden paintComponent() method, we phone call super.paintComponent() to pigment the background of the JComponent. If this call is omitted, y'all must either paint the background yourself (via a fillRect() call) or utilise setOpaque(false) to make the JComponent transparent. This will inform Swing system to paint those JComponents behind the transparent component.
  • We choose the JFrame every bit the source of the KeyEvent. JFrame shall be "in focus" when the cardinal is pressed. The requestFocus() method (of "this" JFrame) is invoked to request for the keyboard focus.

[TODO]: may need to revise.

Try

Graphics_MoveABall.png

Modifying the program to move a ball in response to up/down/left/correct buttons, every bit well as the 4 pointer and "wasd" keys , as shown:

Instance 2: Moving Sprites

In game programming, we have moving game objects called sprites. Each sprite is normally modeled in its ain class, with its own properties, and it can pigment itself.

Sprite.java

This class models a sprite, with its own properties, and it tin can paint itself via the paint() method provided given a Graphics context. A rectangle is used here.

ane ii iii 4 5 half dozen 7 8 9 10 11 12 xiii 14 15 16 17 18 nineteen 20 21 22 23 24 25
import java.awt.*;     public class Sprite {        int ten, y, width, pinnacle;     Colour colour = Color.Blood-red;           public Sprite(int 10, int y, int width, int height, Color color) {       this.10 = 10;       this.y = y;       this.width = width;       this.height = meridian;       this.color = colour;    }          public void pigment(Graphics thou) {       g.setColor(color);       1000.fillRect(ten, y, width, height);     } }
MoveASprite.java

Instead of repainting the entire brandish, we just repaint the affected areas (clips), for efficiency, via the repaint(x, y, width, elevation) method. In moveLeft() and moveRight(), we save the states, move the object, repaint the saved clip-area with the background color, and repaint the new clip-expanse occupied by the sprite. Repainting is washed by asking the sprite to paint itself at the new location, and erase from the erstwhile location.

1 2 3 iv v 6 7 8 9 ten xi 12 13 14 xv 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 sixty 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 fourscore 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
import java.awt.*;        import java.awt.upshot.*;  import javax.swing.*;      public class CGMoveASprite extends JFrame {        public static terminal int CANVAS_WIDTH = 400;    public static final int CANVAS_HEIGHT = 140;    public static final Colour CANVAS_BG_COLOR = Color.CYAN;      private DrawCanvas canvas;     private Sprite sprite;               public CGMoveASprite() {              sprite = new Sprite(CANVAS_WIDTH / 2 - five, CANVAS_HEIGHT / two - xl,             x, 80, Color.RED);                JPanel btnPanel = new JPanel(new FlowLayout());       JButton btnLeft = new JButton("Move Left ");       btnPanel.add(btnLeft);       btnLeft.addActionListener(new ActionListener() {          @Override          public void actionPerformed(ActionEvent evt) {             moveLeft();             requestFocus();           }       });       JButton btnRight = new JButton("Move Right");       btnPanel.add(btnRight);       btnRight.addActionListener(new ActionListener() {          @Override          public void actionPerformed(ActionEvent evt) {             moveRight();             requestFocus();           }       });                canvas = new DrawCanvas();       canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));                Container cp = getContentPane();       cp.setLayout(new BorderLayout());       cp.add(canvas, BorderLayout.Eye);       cp.add(btnPanel, BorderLayout.SOUTH);                addKeyListener(new KeyAdapter() {          @Override          public void keyPressed(KeyEvent evt) {             switch(evt.getKeyCode()) {                instance KeyEvent.VK_LEFT:  moveLeft();  pause;                example KeyEvent.VK_RIGHT: moveRight(); break;             }          }       });         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       setTitle("Move a Sprite");       pack();                   setVisible(true);         requestFocus();        }          private void moveLeft() {              int savedX = sprite.10;              sprite.ten -= 10;              sail.repaint(savedX, sprite.y, sprite.width, sprite.height);        canvas.repaint(sprite.10, sprite.y, sprite.width, sprite.height);     }          private void moveRight() {              int savedX = sprite.x;              sprite.x += 10;              canvas.repaint(savedX, sprite.y, sprite.width, sprite.height);        canvas.repaint(sprite.ten, sprite.y, sprite.width, sprite.height);     }          form DrawCanvas extends JPanel {       @Override       public void paintComponent(Graphics g) {          super.paintComponent(g);          setBackground(CANVAS_BG_COLOR);          sprite.paint(thousand);         }    }          public static void main(Cord[] args) {              SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new CGMoveASprite();           }       });    } }

Example iii: Pigment

Graphics_MyPaint.gif

MyPaint.java
i 2 3 iv v 6 7 8 9 10 11 12 13 14 15 xvi 17 18 xix xx 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 threescore 61 62 63 64 65 66 67 68 69 70
import java.util.List; import java.util.ArrayList; import java.awt.*;        import java.awt.upshot.*;  import javax.swing.*;        public form MyPaint extends JFrame {        public static final int CANVAS_WIDTH = 500;    public static concluding int CANVAS_HEIGHT = 300;    public static final Color LINE_COLOR = Color.Reddish;          individual List<PolyLine> lines = new ArrayList<PolyLine>();    private PolyLine currentLine;            public MyPaint() {       DrawCanvas sheet = new DrawCanvas();       canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));       sheet.addMouseListener(new MouseAdapter() {          @Override          public void mousePressed(MouseEvent evt) {                          currentLine = new PolyLine();             lines.add(currentLine);             currentLine.addPoint(evt.getX(), evt.getY());          }       });       canvas.addMouseMotionListener(new MouseMotionAdapter() {          @Override          public void mouseDragged(MouseEvent evt) {             currentLine.addPoint(evt.getX(), evt.getY());             repaint();            }       });         setContentPane(canvas);       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       setTitle("Pigment");       pack();       setVisible(true);    }          private form DrawCanvas extends JPanel {       @Override       protected void paintComponent(Graphics 1000) {           super.paintComponent(g);          thousand.setColor(LINE_COLOR);          for (PolyLine line: lines) {             line.draw(m);          }       }    }          public static void chief(String[] args) {       SwingUtilities.invokeLater(new Runnable() {                    @Override          public void run() {             new MyPaint();           }       });    } }
PolyLine.java
1 2 iii four five half-dozen seven 8 ix 10 11 12 13 14 fifteen 16 17 18 xix 20 21 22 23 24 25 26 27 28 29
import java.awt.Graphics; import java.util.*;  public class PolyLine {    private Listing<Integer> xList;      private List<Integer> yList;            public PolyLine() {       xList = new ArrayList<Integer>();       yList = new ArrayList<Integer>();    }          public void addPoint(int x, int y) {       xList.add(x);       yList.add(y);    }          public void draw(Graphics g) {        for (int i = 0; i < xList.size() - 1; ++i) {          thou.drawLine((int)xList.become(i), (int)yList.get(i), (int)xList.get(i + 1),                (int)yList.get(i + 1));       }    } }
Dissecting the Programme

[TODO]

Drawing Images

javax.swing.ImageIcon

The javax.swing.ImageIcon class represents an icon, which is a fixed-size film, typically small-scale-size and used to decorate components. To create an ImageIcon:

            String imgNoughtFilename = "images/nought.gif"; ImageIcon iconNought = null; URL imgURL = getClass().getClassLoader().getResource(imgNoughtFilename); if (imgURL != zip) {    iconNought = new ImageIcon(imgURL); } else {    System.err.println("Couldn't find file: " + imgNoughtFilename); }

Graphics Form' drawImage()

ImageIcon is fixed-in-sized and cannot exist resized in brandish. Y'all tin utilise Graphics's drawImage() to resize a source epitome in display.

The java.awt.Graphics class declares 6 overloaded versions of abstract method drawImage().

public abstract boolean drawImage(Image          img, int          ten, int          y, ImageObserver          observer) public abstract boolean drawImage(Image          img, int          x, int          y, int          width, int          height, ImageObserver          observer) public abstruse boolean drawImage(Image          img, int          x, int          y, Colour          bgcolor, ImageObserver          observer) public abstruse boolean drawImage(Image          img, int          x, int          y, int          width, int          height, Color          bgcolor, ImageObserver          observer)      public abstract boolean drawImage(Image          img, int          destX1, int          destY1, int          destX2, int          destY2,       int          srcX1, int          srcY1, int          srcX2, int          srcY2, ImageObserver          observer) public abstract boolean drawImage(Epitome          img, int          destX1, int          destY1, int          destX2, int          destY2,       int          srcX1, int          srcY1, int          srcX2, int          srcY2, Colour          bgcolor, ImageObserver          observer)        

Graphics_DrawImage.png

The coordinates involved is shown in the in a higher place diagram. The ImageObserver receives notification almost the Image every bit it is loaded. In nigh purposes, you lot can gear up information technology to zippo or this.

The drawImage() method requires an Epitome instance, which can be obtained via ImageIcon'south getImage() method; or via static method ImageIO.read() (read "Reading Images into your program"). For example,

            ImageIcon icon = nil; String imgFilename = "images/duke.gif"; java.internet.URL imgURL = getClass().getClassLoader().getResource(imgFilename); if (imgURL != null) {          icon =  new ImageIcon(imgURL); } else {    Organization.err.println("Couldn't find file: " + imgFilename); }    final          Image img = icon.getImage();  JLabel lbl4 = new JLabel() {    @Override public void paintComponent(Graphics chiliad) {       super.paintComponent(g);         g.drawImage(img, 0, 0, 200, 200, null);    } }; lbl4.setPreferredSize(new Dimension(200, 200)); cp.add(lbl4);

Example

Graphics_DrawImageDemo.png

Images:

cross.gif nought.gif

1 2 three 4 5 6 7 viii 9 10 11 12 thirteen 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 xxx 31 32 33 34 35 36 37 38 39 xl 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 xc
import java.awt.*;      import java.net.URL; import javax.swing.*;   import java.util.Random;    @SuppressWarnings("serial") public class CGDrawImageDemo extends JFrame {        public static final int ROWS = 3;    public static final int COLS = iii;    public static last int IMAGE_SIZE = 50;    public static last int PADDING = 20;      public static terminal int CELL_SIZE = IMAGE_SIZE + 2 * PADDING;    public static final int CANVAS_SIZE = CELL_SIZE * ROWS;      private DrawCanvas canvas;        private Random random = new Random();           private String imgCrossFilename = "images/cantankerous.gif";    individual String imgNoughtFilename = "images/nought.gif";    private Image imgCross;       private Image imgNought;          public CGDrawImageDemo() {              ImageIcon iconCross = null;       ImageIcon iconNought = null;       URL imgURL = getClass().getClassLoader().getResource(imgCrossFilename);       if (imgURL != null) {          iconCross = new ImageIcon(imgURL);       } else {          System.err.println("Couldn't find file: " + imgCrossFilename);       }       imgCross = iconCross.getImage();         imgURL = getClass().getClassLoader().getResource(imgNoughtFilename);       if (imgURL != naught) {          iconNought = new ImageIcon(imgURL);       } else {          Arrangement.err.println("Couldn't find file: " + imgNoughtFilename);       }       imgNought = iconNought.getImage();         sheet = new DrawCanvas();       canvas.setPreferredSize(new Dimension(CANVAS_SIZE, CANVAS_SIZE));       setContentPane(canvas);         setDefaultCloseOperation(EXIT_ON_CLOSE);       pack();         setTitle("Examination drawImage()");       setVisible(truthful);    }          private class DrawCanvas extends JPanel {       @Override       public void paintComponent(Graphics g) {          super.paintComponent(g);          setBackground(Color.WHITE);                      for (int row = 0; row < ROWS; ++row) {             for (int col = 0; col < COLS; ++col) {                boolean useCross = random.nextBoolean();                Image img = useCross ? imgCross : imgNought;                1000.drawImage(img,                      CELL_SIZE * col + PADDING, CELL_SIZE * row + PADDING,                      IMAGE_SIZE, IMAGE_SIZE, goose egg);             }          }                    k.fill3DRect(CELL_SIZE - 2, 0, 4, CELL_SIZE * 3, true);          g.fill3DRect(CELL_SIZE * 2 - 2, 0, four, CELL_SIZE * 3, true);          g.fill3DRect(0, CELL_SIZE - ii, CELL_SIZE * 3, 4, true);          g.fill3DRect(0, CELL_SIZE * 2 - 2, CELL_SIZE * iii, 4, truthful);       }    }          public static void main(String[] args) {              SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new CGDrawImageDemo();           }       });    } }

This instance places absolute numbers in the draw methods, which is hard to maintain and reuse. Y'all should ascertain name-constants such every bit CELL_WIDTH, BORDER_WIDTH, etc, and compute the numbers based on these constants.

Animation

Blitheness using javax.swing.Timer

Graphics_BouncingBall.png

Creating an blitheness (such as a bouncing ball) requires repeatedly running an updating job at a regular interval. Swing provides a javax.swing.Timer class which can be used to fire ActionEvent to its registered ActionListeners at regular interval.

The Timer class has i constructor:

public Timer(int          filibuster, ActionListener          listener)

You are required to override the actionPerformed() method of the ActionListener to specify your task's behavior. The Timer fires an ActionEvent to the ActionListener after the (initial) delay, and and so at regular interval afterwards delay.

You tin can beginning and finish the Timer via the Timer's start() and stop() methods. For instance,

int filibuster = 500;   ActionListener updateTask = new ActionListener() {    @Override    public void actionPerformed(ActionEvent evt) {           } };  new Timer(filibuster, updateTask).get-go();

You can apply method setRepeats(false) to prepare the Timer to burn down only one time, after the filibuster. You tin set the initial delay via setInitialDelay() and regular delay via setDelay().

A Timer tin can fire the ActionEvent to more than i ActionListenersouthward. Yous can register more ActionListeners via the addActionListener() method.

The actionPerformed() runs on the effect-dispatching thread, just like all the event handlers. You can be relieved of the multi-threading bug.

JDK i.three introduced another timer grade called coffee.util.Timer, which is more general, but javax.swing.Timer is sufficient (and easier) to run animation in Swing application.

Example: A Bouncing Brawl
1 2 three 4 5 six 7 8 nine 10 11 12 13 14 15 xvi 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 forty 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
import coffee.awt.*;        import coffee.awt.issue.*;  import javax.swing.*;        @SuppressWarnings("series") public form CGBouncingBallSwingTimer extends JFrame {        private static terminal int CANVAS_WIDTH = 640;    individual static final int CANVAS_HEIGHT = 480;                  individual static final int UPDATE_PERIOD = 50;                        private DrawCanvas canvas;            individual int x = 100, y = 100;      private int size = 250;            individual int xSpeed = 3, ySpeed = five;           public CGBouncingBallSwingTimer() {       sail = new DrawCanvas();       sheet.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));       this.setContentPane(sail);       this.setDefaultCloseOperation(EXIT_ON_CLOSE);       this.pack();       this.setTitle("Bouncing Brawl");       this.setVisible(true);                           ActionListener updateTask = new ActionListener() {          @Override          public void actionPerformed(ActionEvent evt) {             update();                                    repaint();                                    }       };                                    new Timer(UPDATE_PERIOD, updateTask).start();                  }          public void update() {       x += xSpeed;       y += ySpeed;       if (x > CANVAS_WIDTH - size || x < 0) {          xSpeed = -xSpeed;       }       if (y > CANVAS_HEIGHT - size || y < 0) {          ySpeed = -ySpeed;       }    }          individual class DrawCanvas extends JPanel {       @Override       public void paintComponent(Graphics g) {          super.paintComponent(yard);            setBackground(Color.Blackness);          g.setColor(Color.BLUE);          m.fillOval(x, y, size, size);         }    }          public static void main(String[] args) {              SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new CGBouncingBallSwingTimer();           }       });    } }

javax.swing.Timer does non provide very accurate timing due to the overhead of event-handling. It probaly cannot be used for real-fourth dimension awarding such equally displaying a clock.

[TODO] Stop the Timer later on ten steps

(Advanced) Animation using a new Thread

Graphics_BouncingBall.png

Blitheness normally involves multi-threading, so that the GUI refreshing operations does not interfere with the programming logic. Multi-threading is an avant-garde topics. Read "Multithreading & Concurrent Programming"

In the previous instance, nosotros utilise javax.swing.Timer, which run the updating job at regular interval on the result-dispatching thread. In this example, we shall create a new thread to run the update.

To create a new thread, define a (bearding and inner) subclass of Thread and override the run() method to specify the behavior of the task. Create an instance and get-go the instance via the start() method, which calls dorsum the run() defined earlier.

To ensure the new thread does not starve the other threads, in particular the event-dispatching thread, the thread shall yield control via the sleep(mills) method, which as well provides the necessary filibuster.

ane 2 3 four 5 6 7 viii 9 x eleven 12 13 14 15 xvi 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 fifty 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
import java.awt.*;     import javax.swing.*;     public class CGBouncingBall extends JFrame {        individual static final int CANVAS_WIDTH = 640;    private static final int CANVAS_HEIGHT = 480;                  private static terminal int UPDATE_INTERVAL = l;                                    individual DrawCanvas canvas;            individual int x = 100;         private int y = 100;    private int size = 250;      private int xSpeed = 3;      individual int ySpeed = v;            public CGBouncingBall() {       canvas = new DrawCanvas();       sheet.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));       this.setContentPane(canvass);       this.setDefaultCloseOperation(EXIT_ON_CLOSE);       this.pack();       this.setTitle("Bouncing Ball");       this.setVisible(truthful);                                      Thread updateThread = new Thread() {          @Override          public void run() {             while (true) {                update();                   repaint();                  try {                                      Thread.sleep(UPDATE_INTERVAL);                  } take hold of (InterruptedException ignore) {}             }          }       };       updateThread.first();                                    }          public void update() {       x += xSpeed;       y += ySpeed;       if (x > CANVAS_WIDTH - size || x < 0) {          xSpeed = -xSpeed;       }       if (y > CANVAS_HEIGHT - size || y < 0) {          ySpeed = -ySpeed;       }    }          class DrawCanvas extends JPanel {       @Override       public void paintComponent(Graphics chiliad) {          super.paintComponent(g);            setBackground(Colour.Black);          g.setColor(Color.Blue);          m.fillOval(x, y, size, size);         }    }          public static void primary(Cord[] args) {              SwingUtilities.invokeLater(new Runnable() {          @Override          public void run() {             new CGBouncingBall();           }       });    } }
  • To update the display regularly, we explicitly invoke the repaint() method of the JFrame, which will callback the paintComponent(m) of all the components contained in this JFrame.
  • The display refreshing code is run in its ain thread, so as to avoid the infamous unresponsive user interface problem. It is programmed as an bearding inner course, extends class Thread, past overriding the run() method to provide the programmed operations (i.eastward., repaint()). The commencement() method is apply to starting time the thread, which will callback the run().
  • Inside the overridden run(), the repaint() is programmed inside an infinite loop, followed by a Thread.sleep(milliseconds) method, which suspends the thread for the given milliseconds. This operation provides the necessary delay and also yield control to other thread to perform their intended operations.

[TODO] Stopping the thread after x steps

(Advanced) A Closer Look at repaint()

Reference: "Painting in AWT and Swing" @ http://www.oracle.com/technetwork/java/painting-140037.html. I summarize some of the important points here.

Heavyweight AWT Components vs. Lightweight Swing Components

The original AWT components are heavyweight components. "Heavyweight" ways that the component has it'south ain opaque native window. Heavyweight components, such as coffee.awt.Button, are mapped to the platform-specific components. Information technology relies on the windowing subsystem in each native platform to take care of details such as damage detection, clip calculation, and z-ordering. On the other paw, the newer Swing JComponentsouthward (such as javax.swing.JButton) are lightweight components. A "lightweight" component does not own its screen resources but reuses the native window of its closest heavyweight antecedent. Swing JComponents do non rely on the native platform and are written purely in Coffee, . The top-level containers, such as JFrame, JApplet and JDialog, which are non subclass of JComponent, remain heavyweight. It is because the lightweight Swing JComponents need to adhere to a heavyweight ancestor.

Painting Mechanism

Painting is carried out via a "call-back" mechanism. A plan shall put its painting codes in a overridden method (paint() for AWT components or paintComponent() for Swing component), and the windowing subsystem will phone call dorsum this method when it's time to paint.

System-triggered vs. Application-triggered Painting Requests

There are two types of paint (or repaint) requests:

  1. System-triggered: due east.grand., the component is kickoff made visible, the componet is resized, etc. The windowing subsystem will schedule pigment() or paintComponent() on the upshot-dispatching thread.
  2. Application-triggered: awarding has modified the appearance of the component and requested to repaint the component. All the same, Application shall not invoke paint() or paintComponent() directly. Instead, it shall invoke a special method called repaint(), which will in turn invoke pigment() or paintComponent(). Multiple repaint() requests may be complanate into a unmarried pigment() call.

Instead of issuing repaint() to paint the entire component, for efficiency, you can selectively repaint a rectangular clip area. You can also specify a maximum fourth dimension limit for painting to take identify.

public void repaint()        public void repaint(long timeMax)          public void repaint(int x, int y, int width, int peak)          public void repaint(long timeMax, int x, int y, int width, int meridian)        
Painting the Lightweight Swing Components

A lightweight needs a heavyweight somewhere up the containment hierarchy in order to have a place to paint, every bit only heavyweight components have their own opaque window. When this heavyweight antecedent is asked to paint its window, it must besides paint all of its lightweight descendants. This is handled by java.awt.Container's paint() method, which calls pigment() on any of its visible, lightweight children which intersect with the rectangle to be painted. Hence, it is crucial for all Container subclasses (lightweight or heavyweight) that override paint() to place a super.paint() call in the paint() method. This super.paint() call invoke Container's (super) paint() method, which in turn invoke paint() on all its descendants. If the super.paint() call is missing, some of the lightweight descendants will be shown up.

Opaque and Transparent

Lightweight components does not ain its opaque window and "borrow" the screen real estate of its heavyweight antecedent. As a effect, they could be made transparent, by leaving their background pixels unpainted to permit the underlying component to show through.

To meliorate performance of opaque components, Swing adds a property chosen opaque to all JComponentsouthward. If opaque is set up to true, the component agrees to paint all of the pixels contained inside its rectangular premises. In order words, the windowing subsystem does not accept to do anything within these premises such as painting its ancestors. It opaque is set to simulated, the component makes no guarantees about painting all the $.25 within its rectangular bounds, and the windowing subsystem has more than work to practice.

Swing further factor the paint() method into three methods, which are invoked in the following guild:

protected void paintComponent(Graphics g) protected void paintBorder(Graphics g) protected void paintChildren(Graphics g)

Swing programs should override paintComponent() instead of paint().

Near of the standard Swing components (in particular, JPanel) take their look and experience implemented past separate look-and-feel objects (called "UI delegates") for Swing'due south Pluggable expect and feel feature. This ways that nigh or all of the painting for the standard components is delegated to the UI consul and this occurs in the post-obit mode:

  1. pigment() invokes paintComponent().
  2. If the ui belongings is not-null, paintComponent() invokes ui.update().
  3. If the component's opaque property is true, ui.udpate() fills the component's background with the background color and invokes ui.paint().
  4. ui.paint() renders the content of the component.

This means that subclasses of Swing components which have a UI delegate (such every bit JPanel), should invoke super.paintComponent() inside their overridden paintComponent(), so that ui.update() fills the background (of the superclass such as JPanel) provided opaque is true.

public class MyPanel extends JPanel {    @Override    protected void paintComponent(Graphics g) {       // Allow UI delegate paint first        // (including background filling, if I'm opaque)              super.paintComponent(g);                // paint my contents side by side....    } }

Try removing the super.paintComponent() from a Swing program that does blitheness (e.g., bouncing ball). The background will not be painted, and the previous screen may not be cleared. You tin also pigment the background yourself by filling a Rectangle with groundwork colour.

@Override protected void paintComponent(Graphics thousand) {    g.setColor(backgroundColor);    g.fillRect(0, 0, getWidth() - i, getHeight() - ane); }

Furthermore, if you set the opaque to false (via setOpaque(false)) for the subclass of JPanel, the super.paintComponent(k) does not fill the background.

REFERENCES & Resource

  • "The Swing Tutorial" @ http://docs.oracle.com/javase/tutorial/uiswing/, in particular, the section on "Performing Custom Graphics".
  • "Painting in AWT and Swing" @ http://www.oracle.com/technetwork/java/painting-140037.html.

madirazzaereun1996.blogspot.com

Source: https://www3.ntu.edu.sg/home/ehchua/programming/java/J4b_CustomGraphics.html