在 JComponent 中的图像顶部绘制图像会擦除底部图像的一部分

Drawing an image on top of an image in a JComponent erases part of the bottom image

我正在制作一个 2d 游戏,我需要在另一个图像之上绘制一个图像。在我绘制第一张图片(较大的图片,jpg)后,第二张图片(较小的图片,png)从第二张图片到右下角的位置擦除。像这样:

我对此进行了一些调查,有人建议我使用缓冲图像,所以我对两个图像都这样做了,但问题仍然存在。这是我看过的 post:How to draw an image over another image?。我还看到有人建议使用 graphics2d,但我并没有真正理解使用它们的原因或如何使用它们。我是 java 图形和图像的新手,所以这可能是一个愚蠢的错误。 这是我的代码。谢谢。

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import javax.swing.*;
import java.util.ArrayList;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.IOException;

public class DisplayExample extends JComponent
{
private BufferedImage backgroundImage;
private String backgroundName;

private BufferedImage image;  //image to draw
private int imageX;  //position of left edge of image
private int imageY;  //position of top edge of image

private JFrame frame;

public static void main(String[] args)
{
    DisplayExample example = new DisplayExample();
    example.run();
}

public DisplayExample()
{
    imageX = 200;
    imageY = 200;

    backgroundName = "backgroundShip.jpg";
    URL backgroundURL = getClass().getResource(backgroundName);
    if (backgroundURL == null)
        throw new RuntimeException("Unable to load:  " + backgroundName);
    try{backgroundImage = ImageIO.read(backgroundURL);}catch(IOException ioe){}
    //load image

    String fileName = "explosion.png";
    URL url = getClass().getResource(fileName);
    if (url == null)
        throw new RuntimeException("Unable to load:  " + fileName);
    //image = new ImageIcon(url).getImage();
    try{image = ImageIO.read(url);}catch(IOException ioe){}
    System.out.println(image instanceof BufferedImage);
    setPreferredSize(new Dimension(1040,500));  //set size of drawing region

    //need for keyboard input
    setFocusable(true);  //indicates that WorldDisp can process key presses

    frame = new JFrame();
    frame.getContentPane().add(this);
    frame.pack();
    frame.setVisible(true);
}

public void paintComponent(Graphics g)
{

    super.paintComponent(g);
    if(backgroundImage != null)
        g.drawImage(backgroundImage,0,0,getWidth(), getHeight(), null);
    g.drawImage(image, imageX, imageY, this);  
}

public void run()
{ 
    while(true)
    {
    imageY+=1;
    repaint();
    try{Thread.sleep(100);}catch(Exception e){}
    }
}

}

所以我使用了你的代码,添加了我自己的图像,它对我来说运行良好。

话虽如此,但您可以在某些方面进行改进:

  • 您 运行 有阻塞事件调度线程或使用 run 方法将线程竞争条件引入代码的风险。您应该考虑改用 Swing Timer。有关详细信息,请参阅 How to use Swing Timers。这允许您安排在 EDT 上下文中调用的定期回调,从而更安全地更新 UI
  • 的上下文
  • 您应该只在 EDT 上下文中创建或修改 UI 的状态,Swing 不是线程安全的。有关详细信息,请参阅 Initial Threads。当 UI 未在 EDT
  • 中初始化时,已知 Swing 具有 "issues"
  • 缩放图像代价高昂,您应该避免在 paint 方法中这样做,而是缩放图像并保留对结果的引用,并在需要绘制时使用它。
  • 您应该考虑使用键绑定 API 而不是 KeyListener,它将解决许多与使用 KeyListener 相关的问题。有关详细信息,请参阅 How to Use Key Bindings

例如...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class DisplayExample extends JComponent {

    private BufferedImage backgroundImage;
    private String backgroundName;

    private BufferedImage image;  //image to draw
    private int imageX;  //position of left edge of image
    private int imageY;  //position of top edge of image

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                DisplayExample example = new DisplayExample();

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(example);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public DisplayExample() {
        imageX = 200;
        imageY = 200;

        try {
            backgroundImage = ImageIO.read(new File("..."));
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        //load image

        try {
            image = ImageIO.read(new File("..."));
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        //need for keyboard input
        //setFocusable(true);  //indicates that WorldDisp can process key presses
        // Use the key bindings API instead, causes less issues
        Timer timer = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                imageY += 1;
                repaint();
            }
        });
        timer.start();
    }

    @Override
    public Dimension getPreferredSize() {
        return backgroundImage == null ?  new Dimension(200, 200) : new Dimension(backgroundImage.getWidth(), backgroundImage.getHeight());
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        if (backgroundImage != null) {
            // Scaling is expensive, don't do it here
            int x = (getWidth() - backgroundImage.getWidth()) / 2;
            int y = (getHeight() - backgroundImage.getHeight()) / 2;
            g2d.drawImage(backgroundImage, x, y, this);
        }
        g2d.drawImage(image, imageX, imageY, this);
        g2d.dispose();
    }
}