3

我在 Java Applet 中创建了一个简单的记忆游戏。我的卡有问题。图像在第一次出现时需要一些额外的加载时间。如何解决?我需要在卡片翻转后立即显示图像。

我显示加载屏幕,直到应用程序不在AppStates.READYAppStates.WAIT_FOR_START状态,但它没有帮助。

Memo.cs - 加载图像的主类

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 - 保存图像和应用程序状态,初始化板

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 - 显示板

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);

                        //...
                    }
                }
            }
        }
    }
}
4

3 回答 3

6

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 (e.g. using Applet.getImage(URL)) the GUI will be notified to repaint as more becomes available.

于 2013-07-25T10:02:40.493 回答
1

Swing当一切准备就绪时,您可以利用双重缓冲并立即显示。

如果您需要在两者之间进行,您可以保留一个预先开发的副本并在需要时显示它。

于 2013-07-25T10:16:58.233 回答
0

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.

于 2013-07-25T10:02:49.087 回答