You are suffering from several problems, DemonPants has pointed out your call to repaint() in the paint method is bad - it will cause the EDT (EventDispatchThread) to be flooded with repaint events continually redrawing your Applet. While this behaviour isn’t inherently bad (it is essentially a primitive form of game loop), it probably isn’t what you are intending.
As to the reason why your 1st sample appears to work, and your 2nd does not - that is a little more complicated.
Lets start with this :-
Component.getImage(…) (which falls through to Toolkit.getImage(…) does return an Image object - however it is returned unloaded. This means it is not ready to be drawn to the screen.
The data for the Image only begins to be loaded the first time you try and use the Image (in your case, this is the first time you attempt to draw it). This loading process however occurs asynchronously, and until it is complete any attempt to draw the image will result in nothing happening.
This is the cause of your 2nd code example not working - your init() method is invoked only once, therefore your first attempt to copy the “dude.png” onto your “i Image” results in failure because the image has yet to be loaded.
As to the reason your 1st code example DOES work - this is yet more complex!
If we go back to:-
[quote]this.getImage(new URL(this.getCodeBase(), “dude.png”))
[/quote]
if you take a look at the javadoc for Toolkit.getImage(…) you will see the documentation mentions that it attempts to cache requests for the same image:
This cache is unwittingly the reason why your first code example works. As your first example repeatedly attempts to load the same image each repaint cycle, the Toolkits underlying cache will be returning you the same Image object. Eventually the Image’s loading will complete and the subsequent copy onto the “i Image” will be successful.
I’ll annotate your code to better explain the exact sequence of steps, so you can better see this:
public class AppTest extends Applet
{
Image i;
public void init() // Called by the browser or applet viewer to inform this applet that it has been loaded into the system.
{
// once this method completes, a repaint event will be pushed onto the EDT (EventDispatchThread), so at some future point the paint(Graphics) method will be called.
}
public void paint(Graphics g) // called by the EDT each time it encounters a paint event in it's queue
{
// LABEL 1
i = createImage(this.getWidth(), this.getHeight()); // creates an empty image
Graphics b = i.getGraphics(); // gets the graphics context to draw onto the empty image
try
{
// attempts to get the image "dude.png" from the Toolkit's image cache
// if it isn't present, it will be added.
Image img = this.getImage(new URL(this.getCodeBase(), "dude.png"));
// attempts to draw the image
// if the image is completely loaded, it will be drawn, otherwise no drawing will occur.
// for images that are not completely loaded, the loading process will commence (if it hasn't already)
b.drawImage(img, 0, 0, null);
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
b.dispose();
// draws the (potencially empty) image to the screen
g.drawImage(i, 0, 0, null);
// pushes another repaint event onto the EDT queue - for the sake of simplicity, just imagine flow-of-control returns to the beginning of this paint(Graphics) method. (GOTO LABEL 1)
repaint();
}
}
Here is a modified version of your code, i’ve left the repaint() call at the end of the paint method, so you can see it allows for a primitive way of animating.
import java.applet.Applet;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.net.MalformedURLException;
import java.net.URL;
public class DudeApplet extends Applet {
Image dude;
public void init()
{
try {
dude = getImage(new URL(this.getCodeBase(), "dude.png"));
// this class provides a mechanism for waiting on a set of resources to load.
MediaTracker imageLoader = new MediaTracker(this);
// adds the dude png to the list of images it is tracking
imageLoader.addImage(dude, 0);
// now initiate the loading, and wait.
imageLoader.waitForAll();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Overridden because we don't want the default behaviour of clearing
* the Component to it's background color. (which by default is white)
* @see Component#update(Graphics)
*/
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g)
{
try {
// note this is a VERY BAD way of throttling,
// it is halting the EDT for 100ms,
// preventing it from processing any other user inputs. (keyboard, mouse etc etc)
// you should replace this with a proper
// game loop & frame rate throttling mechanism
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
// redraw the dude image @ a random location
g.drawImage(dude,
(int)((getWidth()-dude.getWidth(null)) *Math.random()),
(int)((getWidth()-dude.getWidth(null)) *Math.random()), null);
// this is essentially a very primitive way of making a game loop.
repaint();
}
}