How to create a background and foreground image which overlaps?

This solution also addresses the issues mentioned in: Images In JFrame are overwriting each other not displaying both images over eachother


If we try to add a background and some foreground images, it can be a little tricky if we intend to let those images overlap each other as many layouts provided by Java may prevent components (such as JLabels) from overlapping each other. Positioning the images to the exact location can be an issue too.

I will suggest a different approach when we want to create a screen similar to those we see in games:

enter image description here

Instead of creating multiple JLabel filled with imageIcon, an alternative will be drawing directly on the panel. This is a customized panel with instances of images we are interested to draw.

class DrawingSpace extends JPanel
{
    private BufferedImage bg, hero;
    private int bgWidth, bgHeight;
    private int heroWidth, heroHeight;  
    private int scWidth, scHeight;
    private int mouseX, mouseY;

    public DrawingSpace(){      
        loadImages();
        init();     
        setPreferredSize(new Dimension(scWidth, scHeight));     
        addMouseMotionListener(new MouseHandler());     
    }

    private void init(){
        mouseX = 0;
        mouseY = 0;
        heroWidth = hero.getWidth();
        heroHeight = hero.getHeight();      
        bgWidth = bg.getWidth();
        bgHeight = bg.getHeight();      
        scWidth = bgWidth;
        scHeight = bgHeight;        
    }

    private void loadImages(){
        try{
            bg = ImageIO.read(getClass().getResource("Images/background.jpg"));
            hero = ImageIO.read(getClass().getResource("Images/knight.png"));
        }catch(IOException ioe){System.out.println("Unable to open file");}
    }

    @Override public void paintComponent(Graphics g){
        super.paintComponent(g);        
        g.drawImage(bg, 0, 0, bgWidth, bgHeight, null);
        g.drawImage(hero, mouseX-(heroWidth/2), mouseY-(heroHeight/2), heroWidth, heroHeight, null);        
    }

    private class MouseHandler implements MouseMotionListener
    {
        @Override public void mouseMoved(MouseEvent e){
            mouseX = e.getX();
            mouseY = e.getY();
            repaint();
        }
        @Override public void mouseDragged(MouseEvent e){}  
    }
}

A runner class to drive the codes:

class KnightRunner
{   
    public static void main(String[] args){

        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run(){
                JFrame frame = new JFrame("Knight Runner");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       
                frame.add(new DrawingSpace());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);             
            }
        });
    }       
}

Leave a Comment