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
.
Graphics Class' Cartoon Methods
The Graphics
class provides methods for drawing three types of graphical objects:
- Text strings: via the
drawString()
method. Have annotation thatOrganisation.out.println()
prints to the system console, not to the graphics screen. - Vector-graphic primitives and shapes: via methods
drawXxx()
andfillXxx()
, where30
could beLine
,Rect
,Oval
,Arc
,PolyLine
,RoundRect
, or3DRect
. - 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 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
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
(calledDrawCanvas
) and overrides thepaintComponent(Graphics g)
method to do your own drawing with the drawing methods provided past theGraphics
course. -
DrawCanvas
is designed equally an inner form of thisJFrame
application, so as to facilitate admission of the individual variables/methods. - Coffee Windowing Subsystem invokes (calls back)
paintComponent(g)
to render theJPanel
, with the current graphics context ing
, 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()
andm.fillXxx()
) on the current graphics contextg
to perform custom painting on theJPanel
. - The size of the
JPanel
is fix via thesetPreferredSize()
. TheJFrame
does non set its size, but packs the components contained viapack()
. - In the
main()
, the constructor is called in the event-dispatch thread via static methodjavax.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
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
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()
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
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 asJButton
orJLabel
). In this example, we extend theJPanel
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 thedrawLine()
method to depict a line from(x1,y1)
to(x2, y2)
. - The
paintComponent()
method cannot exist called directly from your code, because it requires aGraphics
object as argument. -
paintComponent()
is a and then-chosen "call-back" method. The Windowing subsystem invokes this method and provides a pre-configuredGraphics
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 arepaint()
request. Nether both cases, the Windowing subsystem volition call-back thepaintComponent()
to render the contents of the component with a properGraphics
object as argument. - In this instance, the application requests for a
repaint()
in theKeyEvent
andMouseEvent
handlers, which triggers thepaintComponent()
with an appropriateGraphics
object as the argument. - To exist precise, when you invoke the
repaint()
method to repaint aJComponent
, the Windowing subsystem calls-dorsumpaint()
method. Thepaint()
method and so calls-back three methods:paintComponent()
,paintBorder()
andpaintChilden()
. - In the overridden
paintComponent()
method, we phone callsuper.paintComponent()
to pigment the background of theJComponent
. If this call is omitted, y'all must either paint the background yourself (via afillRect()
call) or utilisesetOpaque(false)
to make theJComponent
transparent. This will inform Swing system to paint thoseJComponents
behind the transparent component. - We choose the
JFrame
every bit the source of theKeyEvent
.JFrame
shall be "in focus" when the cardinal is pressed. TherequestFocus()
method (of "this
"JFrame
) is invoked to request for the keyboard focus.
[TODO]: may need to revise.
Try
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
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)
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
Images:
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
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 ActionListener
southward. Yous can register more ActionListener
s 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
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 theJFrame
, which will callback thepaintComponent(m)
of all the components contained in thisJFrame
. - 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 therun()
method to provide the programmed operations (i.eastward.,repaint()
). Thecommencement()
method is apply to starting time the thread, which will callback therun()
. - Inside the overridden
run()
, therepaint()
is programmed inside an infinite loop, followed by aThread.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 JComponent
southward (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 JComponent
s 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 JComponent
s 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:
- System-triggered: due east.grand., the component is kickoff made visible, the componet is resized, etc. The windowing subsystem will schedule
pigment()
orpaintComponent()
on the upshot-dispatching thread. - Application-triggered: awarding has modified the appearance of the component and requested to repaint the component. All the same, Application shall not invoke
paint()
orpaintComponent()
directly. Instead, it shall invoke a special method calledrepaint()
, which will in turn invokepigment()
orpaintComponent()
. Multiplerepaint()
requests may be complanate into a unmarriedpigment()
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 JComponent
southward. 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:
-
pigment()
invokespaintComponent()
. - If the
ui
belongings is not-null
,paintComponent()
invokesui.update()
. - If the component's
opaque
property istrue
,ui.udpate()
fills the component's background with the background color and invokesui.paint()
. -
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