The idea behind the graphics of an applet is that you paint the pixels
of the rectangular part of the screen that is controlled by the applet.
As Java is an object-oriented language, a graphics object is attached to
the applet, and there is a paint
method that will take care of the coloring of the pixels.
The pixels are counted downward and to the right and the upper left corner is the origin. This direction is convention in computer graphics, and is the same in which you read a book. Note it is not the mathematical convention of graphics, where the origin is in the lower left corner.
There is not only a graphics object for the applet, there is
a graphics object attached to any component. Often
it is called the graphics context.
It contains several instance variables, that determine the
effect of drawing routines like drawText
and drawLine
:
setColor
and getColor
setFont
and getFont
clipRect
and
getClipRect
.Most other methods of the Graphics class
are graphics routines like
drawLine
, fillRect
, and drawText
. Note that there
is also a method drawImage
for displaying any GIF- or JPEG images.
In xxx there is more on image loading.
There are two points to note:
public void paint (Graphics g) { ... }but you hardly ever explicitly call it for the graphics object that is attached to the applet in your code (then it should read something like
this.paint(getGraphics())
).
Instead the paint method is called automatically
by the Screen Updater of the applet when a screen refresh is needed.
This is an example of a "callback routine".The paint routine is not only called when the applet starts, it can be useful when the screen needs a refresh afterwards. We distinguish two types of causes for screen refreshes:
update
method
andpaint
method.Similar to the life cycle applet, we can log the paint and update calls in a text area to see when they are exactly done.
paint
methodThe paint
routine is used when a refresh is demanded by an action
coming from the outside. The screen area is erased, colored with
the background color, and the paint routine performs all its steps. This all happens
automatically for instance when the browser moves to an other page, and
back to the page with applet, or when the window is iconified, and deiconified
again. To be more precise, the action concerning the window generates an event,
and the event dispatcher of the applet reacts with a call to paint.
Note that in the Xeyes applet below, the behaviour with respect to outside actions is good, but the internal events coming from the movements of the mouse cause some flickering. The code is in Eyes.java
update
methodWhen the action that requires a screen refresh comes from within the applet,
a more subtle way is possible.
Instead of a complete redrawing of the screen, you can specify precisely
which pixels should be redrawn. For this goal, you can implement the method
update(Graphics g)
. However, the default implementation
of update consists of a complete erase of the screen, followed by
a paint call.
Similar to the paint routine, you will seldomly call the update
directly. If you need a refresh, say because a button
was pressed, you add a repaint()
method to your code for the action.
This will generate an event, and the event dispatcher will call
update as soon as its turn is there. If many repaint calls arrive
at the event dispatcher in a short time interval, they all can be
collapsed, and then only one update call is done. This favours efficiency,
but implies that you have to program the update method quite
carefully.
As a first example we take the movement of the eyes as in the paint method, but now in the update method. We "forget" that the screen is now not redrawn from scratch. The code is UpdatedEyes1.java
As a second example we take a correct implementation. Now the update first colors the old position of the black spots white again. Note that in the beginning the black spots are not there, as in the paint only the white irises are drawn. The code is in UpdatedEyes.java.
The flickering effect that you seen when a series of paint calls
is done, can also be reduced in another way. It is called
buffering, or double buffering to distinguish from a hardware
buffer for the video. The idea is to build up an off-screen image,
and then map it to the screen with a single drawImage
.
A complete erase of the picture is prevented, all pixels get the right
color immediately, and the flickering is much reduced.
The advantage is that is quite simple to use, you just insert some standard code, and adapt the paint and update routines in a standard way. There is a disadvantage which can be relevant if the applet is time critical: with buffering you still draw a complete image. So it might be more efficient to restrict to the part that is really changing. You can do this for instance with help of the clip region, or you can only update the part of the off-screen image that is really changing. Or you can use a clever combination of these approaches.
As example we take again the Xeyes. Note that in the beginning the black spots are not there, as in the paint only the white irises are drawn. The code is in BufferedEyes.java.
public class BufferedEyes extends Applet { ... Dimension offDimension = this.size(); Image offImage = createImage(offDimension.width, offDimension.height); Graphics offGraphics = offImage.getGraphics(); ... }Note that in the BufferdEyes example, the declaration and the instantiation are separate, which makes it more dynamic as the size of the applet might change.
public void update (Graphics g) { offGraphics.setColor(getBackground()); offGraphics.fillRect(0, 0, offDimension.width, offDimension.height); offGraphics.setColor(Color.white); offGraphics.fillOval(100, 100, 100, 100); offGraphics.fillOval(210, 100, 100, 100); offGraphics.setColor(Color.black); offGraphics.fillOval((int)(lx-8), (int)(ly-8), 16, 16); offGraphics.fillOval((int)(rx-8), (int)(ry-8), 16, 16); g.drawImage(offImage, 0, 0, this); }
public void update (Graphics g) { offGraphics.setColor(getBackground()); offGraphics.fillRect(0, 0, offDimension.width, offDimension.height); offGraphics.setColor(Color.white); offGraphics.fillOval(100, 100, 100, 100); offGraphics.fillOval(210, 100, 100, 100); offGraphics.setColor(Color.black); offGraphics.fillOval((int)(lx-8), (int)(ly-8), 16, 16); offGraphics.fillOval((int)(rx-8), (int)(ry-8), 16, 16); g.drawImage(offImage, 0, 0, this); }
public void paint (Graphics g) { update(g); }
Exercise: why do we work here with the update method, and not with the paint method?