Draw a Circle in Java Group
This chapter shows you how y'all can paint your own custom drawing (such every bit graphs, charts, drawings and, in detail, reckoner game avatars) because you cannot discover standard GUI components that meets your requirements. I shall stress that you should attempt to reuse the standard GUI components as far as possible and leave custom graphics every bit 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 every bit the color and font used in drawing, as well as interacting with the underlying operating system to perform the cartoon. In Coffee, custom painting is done via the coffee.awt.Graphics class, which manages a graphics context, and provides a set up of device-contained methods for drawing texts, figures and images on the screen on dissimilar platforms.
The coffee.awt.Graphics is an abstract class, equally the actual deed of drawing is system-dependent and device-dependent. Each operating platform will provide a subclass of Graphics to perform the actual drawing nether the platform, only adjust to the specification defined in Graphics.
Graphics Class' Drawing Methods
The Graphics class provides methods for drawing iii types of graphical objects:
- Text strings: via the
drawString()method. Accept note thatOrganisation.out.println()prints to the system panel, not to the graphics screen. - Vector-graphic primitives and shapes: via methods
drawXxx()andfillXxx(), whereXxxcould existLine,Rect,Oval,Arc,PolyLine,RoundRect, or3DRect. - Bitmap images: via the
drawImage()method.
drawString(String 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 height); drawArc(int xTopLeft, int yTopLeft, int width, int height, int startAngle, int arcAngle); draw3DRect(int xTopLeft, int, yTopLeft, int width, int acme, 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 summit); 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 height, int arcWidth, int arcHeight) fillPolygon(int[] xPoints, int[] yPoints, int numPoint); drawImage(Image img, int xTopLeft, int yTopLeft, ImageObserver obs); drawImage(Image img, int xTopLeft, int yTopLeft, int width, int height, ImageObserver o);
These drawing methods is illustrated below. The drawXxx() methods draw the outlines; while fillXxx() methods make full the internal. Shapes with negative width and elevation volition non exist painted. The drawImage() will be discussed afterwards.
Graphics Course' Methods for Maintaining the Graphics Context
The graphic context maintains states (or attributes) such as the current painting color, the current font for drawing text strings, and the electric current painting rectangular expanse (called clip). Y'all tin use the methods getColor(), setColor(), getFont(), setFont(), getClipBounds(), setClip() to get or set the color, font, and clip area. Any painting outside the clip area is ignored.
void setColor(Colour 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 10, 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 translate(int x, int y) FontMetrics getFontMetrics() FontMetrics getFontMetrics(Font f)
Graphics Coordinate Organisation
In Coffee 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 system, ranging for (0,0) to (width-1, meridian-1) as illustrated.
You can use method getWidth() and getHeight() to recollect the width and height of a component/container. Yous can use getX() or getY() to get the height-left corner (x,y) of this component's origin relative to its parent.
Custom Painting Template
Under Swing, custom painting is usually performed by extending (i.due east., subclassing) a JPanel as the drawing canvas and override the paintComponent(Graphics thousand) method to perform your own drawing with the drawing methods provided by the Graphics class. The Java Windowing Subsystem invokes (calls back) paintComponent(thou) to return the JPanel by providing the current graphics context k, which can be used to invoke the drawing methods. The extended JPanel is often programmed every bit an inner grade of a JFrame application to facilitate access of individual variables/methods. Although we typically draw on the JPanel, you lot tin in fact draw on whatever JComponent (such as JLabel, JButton).
The custom painting code template is as follows:
1 2 iii 4 five six 7 8 9 ten 11 12 thirteen fourteen fifteen 16 17 18 xix 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 java.awt.*; import java.awt.event.*; import javax.swing.*; public class CGTemplate extends JFrame { public static last int CANVAS_WIDTH = 640; public static last int CANVAS_HEIGHT = 480; private DrawCanvas canvas; public CGTemplate() { canvas = new DrawCanvas(); sheet.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); Container cp = getContentPane(); cp.add(canvas); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); setTitle("......"); setVisible(true); } private grade DrawCanvas extends JPanel { @Override public void paintComponent(Graphics chiliad) { super.paintComponent(one thousand); setBackground(Colour.BLACK); g.setColor(Color.Yellow); g.drawLine(30, twoscore, 100, 200); yard.drawOval(150, 180, ten, 10); k.drawRect(200, 210, twenty, 30); g.setColor(Color.RED); 1000.fillOval(300, 310, 30, 50); g.fillRect(400, 350, threescore, 50); g.setColor(Colour.WHITE); g.setFont(new Font("Monospaced", Font.PLAIN, 12)); g.drawString("Testing custom cartoon ...", 10, 20); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new CGTemplate(); } }); } } |
Dissecting the Program
- Custom painting is performed past extending a
JPanel(chosenDrawCanvas) and overrides thepaintComponent(Graphics g)method to do your own drawing with the cartoon methods provided by theGraphicsclass. -
DrawCanvasis designed as an inner grade of thisJFrameawarding, so as to facilitate admission of the private variables/methods. - Java Windowing Subsystem invokes (calls dorsum)
paintComponent(g)to render theJPanel, with the current graphics context ink, whenever there is a need to refresh the display (due east.g., during the initial launch, restore, resize, etc). You can employ the drawing methods (thou.drawXxx()andone thousand.fillXxx()) on the current graphics contextgto perform custom painting on theJPanel. - The size of the
JPanelis set via thesetPreferredSize(). TheJFramedoes not set its size, but packs the components contained viapack(). - In the
primary(), the constructor is called in the result-dispatch thread via static methodjavax.swing.SwingUtilities.invokeLater()(instead of running in the main thread), to ensure thread-rubber and avert deadlock, as recommended past the Swing developers.
(Avant-garde) Anonymous Inner Class for Drawing Canvas
Instead of a named-inner course called DrawCanvas in the previous case, you tin likewise employ an bearding inner class for the drawing canvas, if the painting code is brusk. For example,
JPanel canvass = new JPanel() { @Override public void paintComponent(Graphics m) { super.paintComponent(g);
...... } }; ...... (Advanced) Getting the Graphics Context
Y'all tin think the Graphics context of a JComponent via the getGraphics() method. This is, however, non ordinarily used. For example,
JPanel console = new JPanel(); Graphics graphics = panel.getGraphics();
Custom Painting in AWT (Obsolete)
Under AWT, yous tin can perform custom painting past extending java.awt.Canvass, and override the paint(Graphics g) method, in a java.awt.Frame awarding. Similarly, you can explicitly invoke repaint() to update the graphics.
Refreshing the Brandish via repaint()
At times, we demand to explicitly refresh the display (east.g., in game and blitheness). Nosotros shall Not invoke paintComponent(Graphics) straight. Instead, we invoke the JComponent's repaint() method. The Windowing Subsystem will in plow remember the paintComponent() with the current Graphics context and execute it in the upshot-dispatching thread for thread safety. You lot tin repaint() a detail JComponent (such equally a JPanel) or the unabridged JFrame. The children contained within the JComponent volition besides be repainted.
Colors and Fonts
java.awt.Color
The class java.awt.Colour provides 13 standard colors equally named-constants. They are: Colour.RED, Green, BLUE, MAGENTA, CYAN, Yellowish, Black, WHITE, Grayness, DARK_GRAY, LIGHT_GRAY, Orange, and PINK. (In JDK one.1, these abiding names are in lowercase, e.k., red. This violates the Java naming convention for constants. In JDK ane.two, the majuscule names are added. The lowercase names were non removed for backward compatibility.)
You can use the toString() to impress the RGB values of these color (e.m., Organization.out.println(Colour.Ruby-red)):
RED : java.awt.Color[r=255, g=0, b=0] GREEN : java.awt.Color[r=0, one thousand=255, b=0] BLUE : java.awt.Color[r=0, g=0, b=255] Yellow : java.awt.Color[r=255, g=255, b=0] MAGENTA : java.awt.Color[r=255, 1000=0, b=255] CYAN : java.awt.Color[r=0, thousand=255, b=255] WHITE : java.awt.Color[r=255, g=255, b=255] Black : coffee.awt.Color[r=0, chiliad=0, b=0] Grayness : java.awt.Color[r=128, g=128, b=128] LIGHT_GRAY: coffee.awt.Color[r=192, k=192, b=192] DARK_GRAY : java.awt.Color[r=64, 1000=64, b=64] Pink : java.awt.Color[r=255, g=175, b=175] ORANGE : java.awt.Color[r=255, one thousand=200, b=0]
You can also utilize the RGB values or RGBA value (A for alpha to specify transparency/opaque) to construct your own color via constructors:
Colour(int r, int g, int b); Color(float r, float thou, float b); Color(int r, int chiliad, int b, int alpha); Color(float r, float g, bladder b, float blastoff);
For case:
Color 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 can use getRed(), getGreen(), getBlue(), getAlpha(), etc.
To fix the background and foreground (text) color of a component/container, y'all can invoke:
JLabel label = new JLabel("Test"); label.setBackground(Color.LIGHT_GRAY); label.setForeground(Color.Ruby-red); To set the color of the Graphics context g (for drawing lines, shapes, and texts), utilise chiliad.setColor(color):
k.setColor(Color.Crimson); g.drawLine(10, xx, 30, twoscore); Colour myColor = new Color(123, 111, 222); g.setColor(myColor); grand.drawRect(x, 10, xl, 50);
(Avant-garde) JColorChooser Example
This example uses the javax.swing.JColorChooser to ready the groundwork color of the JPanel.
1 2 iii 4 5 6 seven viii 9 10 11 12 13 14 fifteen 16 17 xviii 19 20 21 22 23 24 25 26 27 28 29 xxx 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 l | import coffee.awt.*; import java.awt.event.*; import javax.swing.*; @SuppressWarnings("serial") public class JColorChooserDemo extends JFrame { JPanel panel; Colour bgColor = Color.LIGHT_GRAY; public JColorChooserDemo() { console = new JPanel(new BorderLayout()); JButton btnColor = new JButton("Change Colour"); panel.add(btnColor, BorderLayout.Due south); btnColor.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { Color colour = JColorChooser.showDialog(JColorChooserDemo.this, "Choose a color", bgColor); if (colour != null) { bgColor = color; } console.setBackground(bgColor); } }); setContentPane(panel); 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 grade java.awt.Font represents a specific font face, which tin can be used for rendering texts. You can use the post-obit constructor to construct a Font instance:
public Font(Cord name, int style, int size);
You lot tin can use the setFont() method to set the current font for the Graphics context m for rendering texts. For example,
Font myFont1 = new Font(Font.MONOSPACED, Font.Patently, 12); Font myFont2 = new Font(Font.SERIF, Font.BOLD | Font.ITALIC, 16); JButton btn = new JButton("RESET"); btn.setFont(myFont1); JLabel lbl = new JLabel("Hello"); lbl.setFont(myFont2); ...... grand.drawString("In default Font", 10, xx); Font myFont3 = new Font(Font.SANS_SERIF, Font.ITALIC, 12); thou.setFont(myFont3); g.drawString("Using the font set up", 10, fifty); Font's Family Proper name vs. Font Name
A font could accept many faces (or style), e.g., plain, bold or italic. All these faces accept like typographic pattern. The font confront proper name, or font name for short, is the proper noun of a detail font confront, like "Arial", "Arial Bold", "Arial Italic", "Arial Bold Italic". The font family name is the name of the font family that determines the typographic design across several faces, like "Arial". For example,
coffee.awt.Font[family unit=Arial,name=Arial,style=plain,size=ane] java.awt.Font[family=Arial,proper noun=Arial Bold,style=obviously,size=ane] java.awt.Font[family=Arial,proper name=Arial Bold Italic,style=plain,size=1] java.awt.Font[family=Arial,name=Arial Italic,style=plain,size=1]
Logical Font vs. Physical Font
JDK supports these logical font family names: "Dialog", "DialogInput", "Monospaced", "Serif", or "SansSerif". JDK 1.half dozen provides these String constants: Font.DIALOG, Font.DIALOG_INPUT, Font.MONOSPACED, Font.SERIF, Font.SANS_SERIF.
Physical font names are actual font libraries such as "Arial", "Times New Roman" in the system.
GraphicsEnvironment's getAvailableFontFamilyNames() and getAllFonts()
You can use GraphicsEnvironment'south getAvailableFontFamilyNames() to listing all the font famiy names; and getAllFonts() to construct all Font instances (with font size of ane pt). For example,
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); Cord[] fontNames = env.getAvailableFontFamilyNames(); for (String fontName : fontNames) { System.out.println(fontName); } Font[] fonts = env.getAllFonts(); for (Font font : fonts) { System.out.println(font); } Font's deriveFont()
You can utilize Font'southward deriveFont() to derive a new Font instance from this Font with varying size, style and others.
public Font deriveFont(bladder 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.BOLD, 12); System.out.println(font); Font fontDerived = font.deriveFont(twenty); System.out.println(fontDerived);
(Advanced) java.awt.FontMetrics
The java.awt.FontMetrics class can be used to mensurate the exact width and pinnacle of the cord for a particular font face, so that y'all tin position the string equally you desire (such as at the center of the screen).
To create a FontMetrics, employ getFontMetrics() methods of the Graphics class, equally follows:
public abstract FontMetrics getFontMetrics(Font f) public abstruse FontMetrics getFontMetrics()
public int getHeight() public int getLeading() public int getAscent() public int getDescent()
The well-nigh commonly-used function for FontMetrics is to mensurate the width of a given String displayed in a certain font.
public int stringWidth(String str)
To centralize a string on the drawing canvass (e.one thousand., JPanel):
public void paintComponent(Graphics thou) { super.paintComponent(one thousand); g.setFont(new Font("Arial", Font.BOLD, thirty)); FontMetrics fm = thousand.getFontMetrics(); String msg = "Hi, world!"; int msgWidth = fm.stringWidth(msg); int msgAscent = fm.getAscent(); int msgX = getWidth() / 2 - msgWidth / 2; int msgY = getHeight() / two + msgAscent / 2; g.drawString(msg, msgX, msgY); } Custom Graphics Examples
Example 1: Moving an Object via Cardinal/Button Activity
This example illustrates how to re-pigment the screen in response to a KeyEvent or ActionEvent.
The display consists of two JPanel in a JFrame, arranged in BorderLayout. The summit console is used for custom painting; the lesser panel holds two JButton arranged in FlowLayout. Clicking the "Motility Right" or "Move Left" buttons moves the line. The JFrame listens to the "Left-arrow" and "Right-arrow" keys, and responses by moving the line left or right.
1 ii 3 4 5 6 7 8 ix 10 eleven 12 thirteen 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 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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | import java.awt.*; import java.awt.event.*; import javax.swing.*; @SuppressWarnings("serial") public class CGMoveALine extends JFrame { public static last int CANVAS_WIDTH = 400; public static terminal int CANVAS_HEIGHT = 140; public static terminal Color LINE_COLOR = Colour.Blackness; public static terminal Colour CANVAS_BACKGROUND = Colour.CYAN; private int x1 = CANVAS_WIDTH / two; private int y1 = CANVAS_HEIGHT / 8; private int x2 = x1; private int y2 = CANVAS_HEIGHT / 8 * 7; private DrawCanvas sheet; 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 -= x; x2 -= x; canvas.repaint(); requestFocus(); } }); JButton btnRight = new JButton("Move Correct"); btnPanel.add together(btnRight); btnRight.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { x1 += 10; x2 += ten; canvas.repaint(); requestFocus(); } }); canvas = new DrawCanvas(); sail.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(canvas, BorderLayout.CENTER); cp.add(btnPanel, BorderLayout.Due south); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent evt) { switch(evt.getKeyCode()) { instance KeyEvent.VK_LEFT: x1 -= 10; x2 -= 10; repaint(); break; instance KeyEvent.VK_RIGHT: x1 += 10; x2 += 10; repaint(); break; } } }); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setTitle("Move a Line"); pack(); setVisible(true); requestFocus(); } class DrawCanvas extends JPanel { @Override public void paintComponent(Graphics m) { super.paintComponent(g); setBackground(CANVAS_BACKGROUND); 1000.setColor(LINE_COLOR); g.drawLine(x1, y1, x2, y2); } } public static void master(Cord[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new CGMoveALine(); } }); } } |
Dissecting the Program
- To practise custom painting, yous have to decide which superclass to use. It is recommended that y'all use a
JPanel(or a more specialized Swing component such asJButtonorJLabel). In this instance, we extend theJPanelto do our custom painting, in an inner class, as follows:class DrawCanvas extends JPanel { @Override public void paintComponent(Graphics m) { super.paintComponent(g); setBackground(CANVAS_BACKGROUND); g.setColor(LINE_COLOR); g.drawLine(x1, y1, x2, y2); } } - The
paintComponent()method is overridden to provide the custom drawing codes. We use thedrawLine()method to depict a line from(x1,y1)to(x2, y2). - The
paintComponent()method cannot be called directly from your code, because it requires aGraphicsobject as argument. -
paintComponent()is a so-called "call-back" method. The Windowing subsystem invokes this method and provides a pre-configuredGraphicsobject to stand for its country (e.g., current colour, font, clip area and etc). There are two kinds of painting: system-triggered painting and application-triggered painting. In a system-trigger painting, the system request 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 be repaint. In an application-triggered painting, the awarding invokes arepaint()request. Nether both cases, the Windowing subsystem will call-back thepaintComponent()to render the contents of the component with a properGraphicsobject as statement. - In this case, the application requests for a
repaint()in theKeyEventandMouseEventhandlers, which triggers thepaintComponent()with an appropriateGraphicsobject every bit the argument. - To be precise, when you lot invoke the
repaint()method to repaint aJComponent, the Windowing subsystem calls-backpaint()method. Thepaint()method and then calls-dorsum three methods:paintComponent(),paintBorder()andpaintChilden(). - In the overridden
paintComponent()method, we callsuper.paintComponent()to paint the background of theJComponent. If this phone call is omitted, you must either pigment the background yourself (via afillRect()call) or usesetOpaque(false)to make theJComponenttransparent. This will inform Swing arrangement to paint thoseJComponentsbackside the transparent component. - We choose the
JFrameas the source of theKeyEvent.JFrameshall exist "in focus" when the central is pressed. TherequestFocus()method (of "this"JFrame) is invoked to request for the keyboard focus.
[TODO]: may need to revise.
Try
Modifying the programme to motion a ball in response to up/downwards/left/correct buttons, besides every bit the four arrow and "wasd" keys , as shown:
Instance 2: Moving Sprites
In game programming, nosotros take moving game objects called sprites. Each sprite is normally modeled in its own grade, with its own properties, and information technology tin can paint itself.
Sprite.java
This class models a sprite, with its own backdrop, and it tin can paint itself via the paint() method provided given a Graphics context. A rectangle is used here.
1 2 3 4 5 6 7 8 9 ten xi 12 13 fourteen 15 16 17 eighteen nineteen 20 21 22 23 24 25 | import java.awt.*; public form Sprite { int ten, y, width, peak; Colour colour = Color.RED; public Sprite(int x, int y, int width, int height, Color color) { this.x = x; this.y = y; this.width = width; this.height = height; this.color = color; } public void pigment(Graphics thousand) { k.setColor(color); g.fillRect(x, y, width, superlative); } } |
MoveASprite.java
Instead of repainting the entire display, we just repaint the affected areas (clips), for efficiency, via the repaint(x, y, width, height) method. In moveLeft() and moveRight(), we save united states, move the object, repaint the saved clip-area with the background color, and repaint the new clip-surface area occupied past the sprite. Repainting is washed by asking the sprite to paint itself at the new location, and erase from the old location.
i 2 3 4 v 6 7 8 9 x 11 12 13 14 xv 16 17 eighteen xix 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 twoscore 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 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.consequence.*; import javax.swing.*; public class CGMoveASprite extends JFrame { public static final int CANVAS_WIDTH = 400; public static final int CANVAS_HEIGHT = 140; public static final Color CANVAS_BG_COLOR = Color.CYAN; private DrawCanvas canvas; private Sprite sprite; public CGMoveASprite() { sprite = new Sprite(CANVAS_WIDTH / two - 5, CANVAS_HEIGHT / 2 - 40, 10, 80, Color.RED); JPanel btnPanel = new JPanel(new FlowLayout()); JButton btnLeft = new JButton("Motion Left "); btnPanel.add(btnLeft); btnLeft.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { moveLeft(); requestFocus(); } }); JButton btnRight = new JButton("Move Right"); btnPanel.add together(btnRight); btnRight.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { moveRight(); requestFocus(); } }); canvas = new DrawCanvas(); sail.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(canvas, BorderLayout.Middle); cp.add together(btnPanel, BorderLayout.Due south); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent evt) { switch(evt.getKeyCode()) { case KeyEvent.VK_LEFT: moveLeft(); break; case 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.x -= 10; canvas.repaint(savedX, sprite.y, sprite.width, sprite.meridian); canvas.repaint(sprite.x, 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); sheet.repaint(sprite.10, sprite.y, sprite.width, sprite.height); } class DrawCanvas extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); setBackground(CANVAS_BG_COLOR); sprite.paint(g); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new CGMoveASprite(); } }); } } |
Example 3: Paint
MyPaint.java
1 2 3 4 5 half dozen 7 8 ix 10 eleven 12 13 fourteen 15 16 17 eighteen nineteen 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 | import coffee.util.List; import java.util.ArrayList; import java.awt.*; import java.awt.effect.*; import javax.swing.*; public class MyPaint extends JFrame { public static concluding int CANVAS_WIDTH = 500; public static terminal int CANVAS_HEIGHT = 300; public static final Color LINE_COLOR = Colour.Ruby; private List<PolyLine> lines = new ArrayList<PolyLine>(); private PolyLine currentLine; public MyPaint() { DrawCanvas sheet = new DrawCanvas(); canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); sail.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("Paint"); pack(); setVisible(true); } private form DrawCanvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(LINE_COLOR); for (PolyLine line: lines) { line.draw(thousand); } } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new MyPaint(); } }); } } |
PolyLine.java
1 2 three iv five vi 7 8 ix x 11 12 13 fourteen fifteen 16 17 18 nineteen 20 21 22 23 24 25 26 27 28 29 | import java.awt.Graphics; import java.util.*; public class PolyLine { private List<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(10); yList.add together(y); } public void describe(Graphics thousand) { for (int i = 0; i < xList.size() - i; ++i) { g.drawLine((int)xList.go(i), (int)yList.get(i), (int)xList.get(i + 1), (int)yList.get(i + 1)); } } } |
Dissecting the Program
[TODO]
Drawing Images
javax.swing.ImageIcon
The javax.swing.ImageIcon class represents an icon, which is a fixed-size picture, typically small-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 != nix) { iconNought = new ImageIcon(imgURL); } else { Arrangement.err.println("Couldn't detect file: " + imgNoughtFilename); } Graphics Class' drawImage()
ImageIcon is fixed-in-sized and cannot be resized in brandish. You can utilise Graphics's drawImage() to resize a source image in display.
The java.awt.Graphics class declares six overloaded versions of abstruse method drawImage().
public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer) public abstract boolean drawImage(Prototype img, int 10, int y, int width, int height, ImageObserver observer) public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) public abstruse boolean drawImage(Image img, int x, int y, int width, int height, Colour 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 abstruse boolean drawImage(Image img, int destX1, int destY1, int destX2, int destY2, int srcX1, int srcY1, int srcX2, int srcY2, Color bgcolor, ImageObserver observer)
The coordinates involved is shown in the above diagram. The ImageObserver receives notification about the Image as information technology is loaded. In nearly purposes, you lot tin set up it to goose egg or this.
The drawImage() method requires an Image instance, which can be obtained via ImageIcon's getImage() method; or via static method ImageIO.read() (read "Reading Images into your plan"). For example,
ImageIcon icon = null; String imgFilename = "images/duke.gif"; coffee.net.URL imgURL = getClass().getClassLoader().getResource(imgFilename); if (imgURL != zippo) { icon = new ImageIcon(imgURL); } else { Organization.err.println("Couldn't find file: " + imgFilename); } final Epitome img = icon.getImage(); JLabel lbl4 = new JLabel() { @Override public void paintComponent(Graphics grand) { super.paintComponent(thousand); grand.drawImage(img, 0, 0, 200, 200, null); } }; lbl4.setPreferredSize(new Dimension(200, 200)); cp.add(lbl4); Example
Images:
i 2 iii 4 5 6 7 8 nine 10 11 12 13 14 fifteen 16 17 18 xix twenty 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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | import java.awt.*; import java.net.URL; import javax.swing.*; import java.util.Random; @SuppressWarnings("serial") public course CGDrawImageDemo extends JFrame { public static final int ROWS = 3; public static final int COLS = 3; public static final int IMAGE_SIZE = 50; public static final int PADDING = xx; public static last int CELL_SIZE = IMAGE_SIZE + 2 * PADDING; public static final int CANVAS_SIZE = CELL_SIZE * ROWS; individual DrawCanvas sail; private Random random = new Random(); individual String imgCrossFilename = "images/cross.gif"; private Cord imgNoughtFilename = "images/nought.gif"; private Paradigm imgCross; individual Prototype 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 discover file: " + imgCrossFilename); } imgCross = iconCross.getImage(); imgURL = getClass().getClassLoader().getResource(imgNoughtFilename); if (imgURL != nix) { iconNought = new ImageIcon(imgURL); } else { Organisation.err.println("Couldn't notice file: " + imgNoughtFilename); } imgNought = iconNought.getImage(); sail = new DrawCanvas(); canvas.setPreferredSize(new Dimension(CANVAS_SIZE, CANVAS_SIZE)); setContentPane(sheet); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); setTitle("Test drawImage()"); setVisible(true); } individual course DrawCanvas extends JPanel { @Override public void paintComponent(Graphics chiliad) { super.paintComponent(1000); setBackground(Color.WHITE); for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { boolean useCross = random.nextBoolean(); Prototype img = useCross ? imgCross : imgNought; g.drawImage(img, CELL_SIZE * col + PADDING, CELL_SIZE * row + PADDING, IMAGE_SIZE, IMAGE_SIZE, null); } } g.fill3DRect(CELL_SIZE - 2, 0, 4, CELL_SIZE * three, truthful); yard.fill3DRect(CELL_SIZE * two - two, 0, iv, CELL_SIZE * 3, true); thousand.fill3DRect(0, CELL_SIZE - ii, CELL_SIZE * three, iv, true); g.fill3DRect(0, CELL_SIZE * 2 - 2, CELL_SIZE * 3, 4, true); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new CGDrawImageDemo(); } }); } } |
This example places absolute numbers in the draw methods, which is difficult to maintain and reuse. Yous should define name-constants such as CELL_WIDTH, BORDER_WIDTH, etc, and compute the numbers based on these constants.
Animation
Blitheness using javax.swing.Timer
Creating an animation (such as a bouncing brawl) requires repeatedly running an updating chore 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 grade has one constructor:
public Timer(int delay, ActionListener listener)
Y'all are required to override the actionPerformed() method of the ActionListener to specify your task'southward beliefs. The Timer fires an ActionEvent to the ActionListener later the (initial) filibuster, and and so at regular interval afterwards delay.
You can start and finish the Timer via the Timer'southward start() and stop() methods. For example,
int delay = 500; ActionListener updateTask = new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { } }; new Timer(filibuster, updateTask).commencement(); You lot tin can employ method setRepeats(imitation) to prepare the Timer to fire only once, after the delay. You tin can ready the initial filibuster via setInitialDelay() and regular filibuster via setDelay().
A Timer can fire the ActionEvent to more than 1 ActionListenersouth. Yous can annals more ActionListeners via the addActionListener() method.
The actionPerformed() runs on the event-dispatching thread, simply like all the result handlers. You can be relieved of the multi-threading bug.
JDK 1.3 introduced some other timer course called coffee.util.Timer, which is more general, but javax.swing.Timer is sufficient (and easier) to run animation in Swing application.
Instance: A Bouncing Ball
1 2 3 4 5 6 7 viii 9 x 11 12 13 14 15 16 17 eighteen 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 twoscore 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 | import coffee.awt.*; import java.awt.effect.*; import javax.swing.*; @SuppressWarnings("serial") public class CGBouncingBallSwingTimer extends JFrame { individual static last int CANVAS_WIDTH = 640; private static final int CANVAS_HEIGHT = 480; individual static concluding int UPDATE_PERIOD = 50; private DrawCanvas canvas; individual int x = 100, y = 100; individual int size = 250; individual int xSpeed = 3, ySpeed = five; public CGBouncingBallSwingTimer() { canvas = new DrawCanvas(); canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); this.setContentPane(sail); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.pack(); this.setTitle("Bouncing Ball"); this.setVisible(true); ActionListener updateTask = new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { update(); repaint(); } }; new Timer(UPDATE_PERIOD, updateTask).beginning(); } public void update() { 10 += xSpeed; y += ySpeed; if (x > CANVAS_WIDTH - size || 10 < 0) { xSpeed = -xSpeed; } if (y > CANVAS_HEIGHT - size || y < 0) { ySpeed = -ySpeed; } } individual course DrawCanvas extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); setBackground(Colour.Black); 1000.setColor(Colour.BLUE); thousand.fillOval(x, y, size, size); } } public static void chief(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new CGBouncingBallSwingTimer(); } }); } } |
javax.swing.Timer does not provide very accurate timing due to the overhead of issue-handling. Information technology probaly cannot be used for real-fourth dimension application such as displaying a clock.
[TODO] Terminate the Timer later x steps
(Avant-garde) Animation using a new Thread
Animation unremarkably involves multi-threading, and so that the GUI refreshing operations does not interfere with the programming logic. Multi-threading is an advanced topics. Read "Multithreading & Concurrent Programming"
In the previous example, we use javax.swing.Timer, which run the updating task at regular interval on the event-dispatching thread. In this example, we shall create a new thread to run the update.
To create a new thread, define a (anonymous and inner) subclass of Thread and override the run() method to specify the behavior of the task. Create an instance and start the example via the start() method, which calls back 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 also provides the necessary filibuster.
1 2 iii 4 five 6 vii viii 9 x xi 12 13 14 15 xvi 17 eighteen nineteen 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 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 { private static final int CANVAS_WIDTH = 640; private static final int CANVAS_HEIGHT = 480; individual static final int UPDATE_INTERVAL = l; private DrawCanvas sail; private int x = 100; individual int y = 100; private int size = 250; private int xSpeed = iii; private int ySpeed = 5; public CGBouncingBall() { canvas = new DrawCanvas(); canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT)); this.setContentPane(canvas); 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(); endeavour { Thread.sleep(UPDATE_INTERVAL); } take hold of (InterruptedException ignore) {} } } }; updateThread.starting time(); } public void update() { ten += 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 g) { super.paintComponent(one thousand); setBackground(Colour.BLACK); g.setColor(Color.BLUE); m.fillOval(x, y, size, size); } } public static void master(Cord[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new CGBouncingBall(); } }); } } |
- To update the display regularly, nosotros explicitly invoke the
repaint()method of theJFrame, which will callback thepaintComponent(one thousand)of all the components contained in thisJFrame. - The display refreshing code is run in its own thread, and so as to avoid the infamous unresponsive user interface problem. Information technology is programmed every bit an anonymous inner class, extends class
Thread, by overriding therun()method to provide the programmed operations (i.e.,repaint()). Theoffset()method is use to start the thread, which will callback therun(). - Inside the overridden
run(), therepaint()is programmed inside an space loop, followed by aThread.sleep(milliseconds)method, which suspends the thread for the given milliseconds. This functioning provides the necessary delay and also yield command to other thread to perform their intended operations.
[TODO] Stopping the thread afterward 10 steps
(Advanced) A Closer Look at repaint()
Reference: "Painting in AWT and Swing" @ http://world wide web.oracle.com/technetwork/coffee/painting-140037.html. I summarize some of the important points hither.
Heavyweight AWT Components vs. Lightweight Swing Components
The original AWT components are heavyweight components. "Heavyweight" ways that the component has it'southward own opaque native window. Heavyweight components, such equally java.awt.Push, are mapped to the platform-specific components. Information technology relies on the windowing subsystem in each native platform to accept care of details such as damage detection, clip adding, and z-ordering. On the other hand, the newer Swing JComponentsouth (such equally javax.swing.JButton) are lightweight components. A "lightweight" component does non own its screen resources but reuses the native window of its closest heavyweight antecedent. Swing JComponents do not rely on the native platform and are written purely in Coffee, . The pinnacle-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 attach to a heavyweight ancestor.
Painting Mechanism
Painting is carried out via a "call-back" machinery. A program shall put its painting codes in a overridden method (paint() for AWT components or paintComponent() for Swing component), and the windowing subsystem volition retrieve this method when it's time to pigment.
System-triggered vs. Awarding-triggered Painting Requests
There are two types of paint (or repaint) requests:
- System-triggered: e.yard., the component is first made visible, the componet is resized, etc. The windowing subsystem will schedule
paint()orpaintComponent()on the upshot-dispatching thread. - Application-triggered: application has modified the appearance of the component and requested to repaint the component. Withal, Awarding shall non invoke
pigment()orpaintComponent()directly. Instead, information technology shall invoke a special method chosenrepaint(), which will in turn invokepaint()orpaintComponent(). Multiplerepaint()requests may be collapsed into a singlepaint()call.
Instead of issuing repaint() to paint the entire component, for efficiency, y'all can selectively repaint a rectangular clip expanse. Y'all can also specify a maximum time limit for painting to take place.
public void repaint() public void repaint(long timeMax) public void repaint(int x, int y, int width, int height) 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 lodge to accept a identify to pigment, equally only heavyweight components accept their own opaque window. When this heavyweight antecedent is asked to pigment its window, information technology must too paint all of its lightweight descendants. This is handled by java.awt.Container's paint() method, which calls paint() on any of its visible, lightweight children which intersect with the rectangle to be painted. Hence, information technology 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) pigment() method, which in turn invoke pigment() on all its descendants. If the super.pigment() phone call is missing, some of the lightweight descendants will exist shown upward.
Opaque and Transparent
Lightweight components does not ain its opaque window and "borrow" the screen real manor of its heavyweight antecedent. As a result, they could exist made transparent, by leaving their background pixels unpainted to allow the underlying component to show through.
To ameliorate performance of opaque components, Swing adds a property called opaque to all JComponentsouth. If opaque is set to true, the component agrees to paint all of the pixels contained within its rectangular premises. In order words, the windowing subsystem does not have to exercise anything within these bounds such as painting its ancestors. It opaque is prepare to false, the component makes no guarantees about painting all the bits inside its rectangular bounds, and the windowing subsystem has more work to do.
Swing farther factor the paint() method into 3 methods, which are invoked in the following order:
protected void paintComponent(Graphics 1000) protected void paintBorder(Graphics thousand) protected void paintChildren(Graphics g)
Swing programs should override paintComponent() instead of paint().
Most of the standard Swing components (in item, JPanel) have their expect and feel implemented by carve up look-and-feel objects (chosen "UI delegates") for Swing's Pluggable look and feel feature. This means that about or all of the painting for the standard components is delegated to the UI delegate and this occurs in the following way:
-
paint()invokespaintComponent(). - If the
uiproperty is non-zilch,paintComponent()invokesui.update(). - If the component's
opaqueproperty istrue,ui.udpate()fills the component's background with the background color and invokesui.paint(). -
ui.paint()renders the content of the component.
This ways that subclasses of Swing components which have a UI delegate (such every bit JPanel), should invoke super.paintComponent() within their overridden paintComponent(), so that ui.update() fills the background (of the superclass such as JPanel) provided opaque is truthful.
public class MyPanel extends JPanel { @Override protected void paintComponent(Graphics g) { // Let UI delegate paint first // (including background filling, if I'yard opaque) super.paintComponent(g); // paint my contents next.... } } Endeavor removing the super.paintComponent() from a Swing plan that does animation (e.g., bouncing ball). The groundwork will not exist painted, and the previous screen may not be cleared. You can also paint the background yourself by filling a Rectangle with background color.
@Override protected void paintComponent(Graphics g) { g.setColor(backgroundColor); k.fillRect(0, 0, getWidth() - ane, getHeight() - 1); } Furthermore, if you lot gear up the opaque to faux (via setOpaque(false)) for the subclass of JPanel, the super.paintComponent(g) does non make full the background.
REFERENCES & Resources
- "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.
thorntongracts1957.blogspot.com
Source: https://www3.ntu.edu.sg/home/ehchua/programming/java/J4b_CustomGraphics.html
0 Response to "Draw a Circle in Java Group"
Post a Comment