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

狂风中的少年 提交于 2020-01-17 05:38:41

问题


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:

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

}


回答1:


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:

  • 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.

For example...

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


来源:https://stackoverflow.com/questions/31931943/drawing-an-image-on-top-of-an-image-in-a-jcomponent-erases-part-of-the-bottom-im

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!