在 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();
}
}
我正在制作一个 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
方法将线程竞争条件引入代码的风险。您应该考虑改用 SwingTimer
。有关详细信息,请参阅 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();
}
}