在JComponent中的图像顶部绘制图像会擦除底部图像的一部分 [英] Drawing an image on top of an image in a JComponent erases part of the bottom image

查看:90
本文介绍了在JComponent中的图像顶部绘制图像会擦除底部图像的一部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am making a 2d game and I need to draw an image on top of another. After I draw the first image(the larger one, jpg), the second image(the smaller one,png) erases from where the second image is to the lower right hand corner. Like this:

我研究了一下,建议使用缓冲图像,所以我对两个图像都这样做了,问题仍然存在.这是我看过的一篇文章:如何在另一张图像上绘制图像?.我也看到有人建议使用graphics2d,尽管我并不十分了解使用它们的原因或使用方法.我是Java图形和图像的新手,所以这可能是一个愚蠢的错误. 这是我的代码.谢谢.

I have looked into this a bit, and it was suggested that I use buffered images, so I did that with both images and the problem remains. Here is one post I looked at: How to draw an image over another image?. I have also seen some people suggesting graphics2d, though I did not really understand the reason to use them or how to use them. I am new to java graphics and images, so it is probably a silly mistake. Here is my code. Thank you.

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){}
    }
}

}

推荐答案

所以我拿了你的代码,添加了自己的图像,它对我来说运行正常.

So I took your code, added my own images and it runs fine for me.

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

Having said that, there are some areas you can improve:

  • 您冒着阻塞事件调度线程或使用run方法将线程竞争条件引入代码的风险.您应该考虑改用Swing Timer.有关更多详细信息,请参见如何使用Swing计时器.这样,您可以安排在EDT上下文中调用的常规回调,从而更安全地更新UI上下文
  • 您仅应在EDT上下文中创建或修改UI的状态,Swing不是线程安全的.有关更多详细信息,请参见初始线程.当未在EDT中初始化UI时,Swing会产生问题"
  • 缩放图像非常昂贵,应该避免从paint方法中进行缩放,而是缩放图像并保留对结果的引用,并在需要绘制图像时使用它.
  • 您应该考虑在KeyListener上使用键绑定API,它将解决与使用KeyListener相关的许多问题.有关更多详细信息,请参见如何使用键绑定. li>
  • You're running the risk of either blocking the Event Dispatching Thread or introducing a thread race condition into your code with your run method. You should consider using a Swing Timer instead. See How to use Swing Timers for more details. This allows you to schedule regular callbacks which are called within the context of the EDT, making it safer to update the context of the UI
  • You should only ever create or modify the state of the UI from within the context of the EDT, Swing is not thread safe. See Initial Threads for more details. Swing has been known to have "issues" when the UI is not initialised within the EDT
  • Scaling an image is expensive, you should avoid doing so from within the paint methods, instead, scale the image and keep a reference to the result and use it when you need to paint it.
  • You should consider using the key bindings API over KeyListener, it will solve many of the issues associated with using KeyListener. See How to Use Key Bindings for more details.

例如...

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

这篇关于在JComponent中的图像顶部绘制图像会擦除底部图像的一部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆