简体   繁体   中英

How to display an images instantly in Java Applet?

I created a simple memory game in Java Applet. I have a problem with cards. Images need some extra load time during the first appearance. How to fix that? I need to show a image instantly after card flip.

I display loading screen until application isn't in AppStates.READY or AppStates.WAIT_FOR_START state but it dosen't help.

Memo.cs - main class with images loading

public class Memo extends JApplet {

    //...   

    public void init() {
        //...   
        try {
            SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { createGUI(); }});
        } 
        catch(Exception e) {
            e.printStackTrace();
        }
    }

    private void createGUI() {
        final Model model = new Model(...);
        final View view = new View(model);

        getContentPane().add(view, BorderLayout.CENTER);
        setBackground(backgroundColor);
        setPreferredSize(new Dimension(width, height));

        model.setLoading(loadImages(loadingPath, format, 1));
        model.setCardsImages(loadImages(cardImagePath, format, 13));
        //...
        model.setAppState(AppStates.PROCESS);

        model.startNewGame();
        view.repaint();
    }

    private Image[] loadImages(String path1, String path2, int count) {
        Image[] imgs = new Image[count];        
        for(int i = 0; i < count; ++i) {
            imgs[i] = getImage(getCodeBase(), path1 + i + path2);
        }
        return imgs;
    }
}

Model.cs - hold images and application state, init board

public class Model {

    //...

    private AppStates appState;
    private Image[] cardsImages;

    public Model(...) {
        //...
        appState = AppStates.INIT;
    }

    public void startNewGame() {
        setAppState(AppStates.PROCESS);

        //... - init board - table with images' id

        setAppState(AppStates.WAIT_FOR_START);
    }

    public void setCardsImages(Image[] cardsImages) {
        this.cardsImages = cardsImages;
    }

    public Image getCardsImage(int v) {
        return cardsImages[v];
    }

    public AppStates getAppState() {
        return appState;
    }

    public void setAppState(AppStates appState) {
        this.appState = appState;
        //...
    }

    //...
}

View.cs - display board

public class View extends JPanel {

    //...

    private Model model;

    public View(Model model) {
        this.model = model;
        //...
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        if(model != null) {
            switch (model.getAppState()) {

            //...

            case WAIT_FOR_START:            
            case READY:
                //...
                drawBoard(g2d, model.getBoard(), model.getStates(), model.getFrontTypes());
                break;

            }
        }
        repaint();
    }

    private void drawBoard(Graphics2D g2d, int[][] board, int[][] states, int[][] frontTypes) {
        if(board != null && states != null && board.length > 0 && states.length > 0) {
            for(int x = 0; x < board.length; x++) {
                for(int y = 0; y < board[x].length; y++) {  
                    if(states[x][y] != Model.HIDE) {
                        Image img = null;

                        //...
                        img = model.getCardsImage(board[x][y]);

                        g2d.drawImage(
                                img,
                                model.getFirstCardX() + x * model.getCardDistance() + x * model.getCardWidth(), 
                                model.getFirstCardY() + y * model.getCardDistance() + y * model.getCardHeight(), 
                                model.getCardWidth(), 
                                model.getCardHeight(),
                                null);

                        //...
                    }
                }
            }
        }
    }
}

From the JavaDocs..

public Image getImage(URL url)

..

This method always returns immediately, whether or not the image exists. When this applet attempts to draw the image on the screen, the data will be loaded.

Use ImageIO.read(URL) instead. This method will 'block' (it will stop at that code line) until the image is read. So when the app. goes to use the image, it has already been completely loaded.

Another way to get around the problem (not quite the way you wanted) is to change..

g2d.drawImage(
    img,
    model.getFirstCardX() + x * model.getCardDistance() + x * model.getCardWidth(), 
    model.getFirstCardY() + y * model.getCardDistance() + y * model.getCardHeight(), 
    model.getCardWidth(), 
    model.getCardHeight(),
    null);

To..

g2d.drawImage(
    img,
    model.getFirstCardX() + x * model.getCardDistance() + x * model.getCardWidth(), 
    model.getFirstCardY() + y * model.getCardDistance() + y * model.getCardHeight(), 
    model.getCardWidth(), 
    model.getCardHeight(),
    this);

If the image is loaded asynchronously (eg using Applet.getImage(URL) ) the GUI will be notified to repaint as more becomes available.

you can take the advantage of double buffering in Swing and displaying at once when everything is ready.

If you need to do it inbetween you can keep a pre-developed copy and displaying it when required.

One way to pre-fetch an image is to use MediaTracker . Using this class guarantees that an image is loaded at a given point. Here's an excellent demonstration from the documentation:

import java.applet.Applet;
import java.awt.Color;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.MediaTracker;

public class ImageBlaster extends Applet implements Runnable {
    MediaTracker tracker;
    Image bg;
    Image anim[] = new Image[5];
    int index;
    Thread animator;

    // Get the images for the background (id == 0)
    // and the animation frames (id == 1)
    // and add them to the MediaTracker
    public void init() {
        tracker = new MediaTracker(this);
        bg = getImage(getDocumentBase(),
                "images/background.gif");
        tracker.addImage(bg, 0);
        for (int i = 0; i < 5; i++) {
            anim[i] = getImage(getDocumentBase(),
                    "images/anim"+i+".gif");
            tracker.addImage(anim[i], 1);
        }
    }

    // Start the animation thread.
    public void start() {
        animator = new Thread(this);
        animator.start();
    }

    // Stop the animation thread.
    public void stop() {
        animator = null;
    }

    // Run the animation thread.
    // First wait for the background image to fully load
    // and paint.  Then wait for all of the animation
    // frames to finish loading. Finally, loop and
    // increment the animation frame index.
    public void run() {
        try {
            tracker.waitForID(0);
          tracker.waitForID(1);
        } catch (InterruptedException e) {
            return;
        }
        Thread me = Thread.currentThread();
        while (animator == me) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                break;
            }
            synchronized (this) {
                index++;
                if (index >= anim.length) {
                    index = 0;
                }
            }
            repaint();
        }
    }

    // The background image fills the frame so we
    // don't need to clear the applet on repaints.
    // Just call the paint method.
    public void update(Graphics g) {
        paint(g);
    }

    // Paint a large red rectangle if there are any errors
    // loading the images.  Otherwise always paint the
    // background so that it appears incrementally as it
    // is loading.  Finally, only paint the current animation
    // frame if all of the frames (id == 1) are done loading,
    // so that we don't get partial animations.
    public void paint(Graphics g) {
        if ((tracker.statusAll(false) & MediaTracker.ERRORED) != 0) {
            g.setColor(Color.red);
            g.fillRect(0, 0, size().width, size().height);
            return;
        }
        g.drawImage(bg, 0, 0, this);
        if (tracker.statusID(1, false) == MediaTracker.COMPLETE) {
            g.drawImage(anim[index], 10, 10, this);
        }
    }
}

If you can guarantee that this will always run on Java 1.4 or later, you can use [ImageIO.read][2] to obtain your images instead. It will wait until the image is loaded.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM