Standard Java graphics are a bit weird. All drawing on the screen results from implicit or explicit calls of repaint()
method, which posts an event on the event queue and eventually results in the paint method being called.
Below we explain how to pop up a window and to create and draw geometric objects in the window. We then see what extra must be done to draw images loaded from a file. Finally we show how to do simple animations using a timer to trigger redrawing of the images.
Here are some details that can best be understood by looking at the program MyGraphicsDemo
.
-
The classes defined below are found in packages
java.awt
andjava.awt.geom
. Thus you will need to include statements to importjava.awt.*
andjava.awt.geom.*
. -
paint(g)
is called automatically when a window is created or uncovered in a window. It can also be forced to execute by sending arepaint()
message to a component. That method schedules a call to anupdate(g)
method as soon as possible. -
The inherited
update(g)
method erases the component and then callspaint(g)
. TheGraphics
objectg
for the component is automatically provided by the system. -
The user overrides
paint(g)
to actually draw something on the component. All drawing commands are either in this method or in a method called frompaint
. -
The
Graphics
object should be thought of like a pen that is responsible for doing the actual drawing. The newer Java graphics actually use a class calledGraphics2D
, which extendsGraphics
, and that is the one actually passed to thepaint
method. Unfortunately, for compatibility with old code, the parameter type is stillGraphics
. To use the newer method described below, you must begin by casting thegraphics
context toGraphics2D
:/** * Draw figures on the window that has graphics g * @param g - the graphics context of the current window */ public void paint(Graphics g) { Graphics2D g2 = (Graphics2D)g; g2.draw(...); ... }
The
draw
message is sent to theGraphics2D
object to draw various framed graphics objects, as shown in the sample code. The methodfill
is used to draw filled objects. The messagedrawString
is used to write a string on the screen. The messagesetPaint(aColor)
changes the color of the pen. It stays that color until changed again. -
The
graphics
objects can be used with either typefloat
ordouble
for coordinates and dimensions. We will always use thedouble
versions. The graphics classes that you should use are thus- Rectangle2D.Double
- RoundRectangle2D.Double
- Ellipse2D.Double
- Arc2D.Double
- Line2D.Double
Their rectangle constructor takes the following parameters:
myRect = new Rectangle2D.Double(x, y, width, height);
The others are similar. Details can be found in the javadoc documentation for the Java library classes. For those of you used to the
objectdraw
library, you should note that there is nocanvas
parameter. Note also that this creates the object, but does not actually draw it anywhere.If
g2
is aGraphics2D
object, then you can actually draw the object using one of the two following methods:g2.draw(myRect); g2.fill(myRect);
The first draws a framed version of the object, while the second draws a filled version.
Every time
repaint
is called or a window is uncovered thepaint
method will be executed. As a result, you should make sure that everything that you want to appear on the screen will be redrawn by thepaint
method. -
You can draw on virtually any component in a window, including a
JApplet
,JFrame
, orJPanel
. Normally in this class we will be drawing on aJFrame
orJPanel
.Suppose you just want to pop up a window and write on its surface. The kind of window we will be using in Java is called a JFrame.1 A program that just pops up a window and draws something on it will have the following form:
public class GraphicsExample extends JFrame { private static final int WINDOW_WIDTH = ...; private static final int WINDOW_HEIGHT = ...; // instance variable declarations /** * Create the window * @ param title -- the text to be shown on the title bar */ public GraphicsExample(String title) { super(title); ... } /** * Draw figures on the window that has graphics g * Called by repaint() or whenever the screen needs to be * refreshed * @param g - the graphics context of the current window */ public paint(Graphics g) { Graphics2D g2 = (Graphics) g; // commands using g2 } // Create the window of the desired size and display it. public static void main(String[] args) { GraphicsExample f = new GraphicsExample("StdGraphicsDemo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(WINDOW_WIDTH, WINDOW_HEIGHT); f.setVisible(true); } }
The class extends
JFrame
, so when it is created it will pop up a window. The call to thesuper
constructor in the constructor forGraphicsExample
ensures that the string in title shows in the title bar of the window. The paint method casts the graphics context to be of type
Graphics2D` so the above commands will work.The
main
method constructs an object of the class, which will result in a window being popped up. The next three commands make sure that clicking in the red "go-away box" at the top of the window will result in the program terminating, set the window size at whatever values are held in the constants, and make the window visible. When the window is made visible for the first time, therepaint
method will automatically be called, which will erase the window and eventually callpaint
with the graphics context of the window.
Often we will want to import images into our program and draw them in a window. This takes a bit more work, and needs the following extra code.
-
You first need to bring the image from a file into computer memory. Unfortunately it can take a (relatively) long time to bring an image from outside of memory (e.g., a
jpeg
orgif
file), so after we request the item to be brought in, we may need to wait for it. I suggest you use the following method to make sure that the image is fully loaded before your program proceeds./** * Retrieve an image from file "filename", return the * Image when it has loaded completely. * @param the name of the file containing the image * @return the loaded Image. */ private Image getCompletedImage(String fileName) { Toolkit toolkit = Toolkit.getDefaultToolkit(); Image myImage = toolkit.getImage(fileName); MediaTracker mediaTracker = new MediaTracker(this); mediaTracker.addImage(myImage, 0); try { mediaTracker.waitForID(0); } catch (InterruptedException ie) { System.out.println(ie); System.exit(1); } return myImage; }
Roughly, the method retrieves a default toolkit that has a method that will allow reading in an image in a file with name given by
fileName
. After the image is fetched with thegetImage
method, aMediaTracker
object is created. The image is added to the media tracker object, which then waits for the loading of the image to be complete. If it fails to complete then an exception will be thrown and the program will exit. Otherwise, when the image is done loading, it will be returned from the method.You can try to do without the
mediaTracker
object, but if you try to access info about the image (e.g., ask its width or height), you might not get accurate information. -
Using the getCompletedImage method, an image can be loaded and displayed in a window with the following code:
Image myImage = getCompletedImage("filename.jpg"); g2.drawImage(flowerImage, x, y, this);
where
filename.jpg
should be replaced by the name of the file holding the image. This code should be placed in apaint(g)
method or in a method called from the paint method.
Animations take a bit of work, as they require a separate thread to do the updating of the picture that is different from the thread that is actually doing the drawing and responding to other events. The following is a description of a low overhead way of doing animation that relies on events fired by a timer rather than creating a separate thread (which we will talk about later in the term). See the program SimpleTimerAnimation
for a simple example of an application that creates an animation using a Timer object.
-
In the constructor for the main class (the one that extends
JFrame
), insert commands of the form Timer myTimer = new Timer(interval,this); myTimer.start();This creates a timer that will generate an
ActionEvent
everyinterval
milliseconds (which is 1/1000 second). The timer can be turned off by sending it astop()
message. -
The constructor given above creates a timer that expects the object that created the timer to respond to the timer events. To be notified that the timer has gone off the class must implement the interface
ActionListener
. Therefore the header of the class should look like:public class MyClass extends JFrame implements ActionListener {
The interface
ActionListener
has a single method:public void actionPerformed(ActionEvent evt);
that the class must implement.
-
Each time the timer is fired (using whatever interval was chosen when the timer was created), the method
actionPerformed
will be called. The value of the formal parameterevt
will be the timer event that triggered the call. It is rarely needed in the method body, so you may ignore it for now, though it must appear as a formal parameter of the method. The body of the method should include whatever is necessary to update the state of all of the objects and then callrepaint()
. -
As before,
repaint()
will end up callingpaint(Graphics g)
. That routine is responsible for doing all of the actualdraw
orfill
commands. Of course that method can itself call draw routines for objects that are in your program. For example, in a program with several gardens, the paint method can send adraw(Graphics g)
message to each of the gardens, where the corresponding method draws all of the plants in the garden. -
In order to clear the screen between invocations of paint, I found it necessary to call
super.paint(g)
at the beginning of thepaint
method. I believe this is necessary so that all the subcomponents also get repainted properly. When this was not done, the earlier images were not erased.
Note: Yes, there is a JWindow
, but it doesn't have a title bar or any of the other gadgets that we associate with modern computer windows.