0

So I am working on a project here that requires a custom JLayeredPane - like class. It has two members 'ground' and 'foreground' that are JPanel and an background (Image) member.

The way it was supposed to show was that the background Image should have been drawn and then all the components of the ground on top of it and then foreground's components at the apex. So foreground covers up ground which covers up background. Background should be shown only at places that do not have a Component in ground and foreground or where there is transparency in the JPanels.

It's paint function goes like this:

@Override
public void paint(Graphics g){
   g.drawImage(background, 0, 0, null);
   ground.paint(g.create());
   foreground.paint(g.create());
   g.dispose();
}

But nothing like that happens. Just the background image gets painted and nothing else shows.

I have used System.out.println() function to check that ground and foreground actually hold components and they do. But they just don't show.

Can anyone help me here?

  • What exactly are `ground` and `foreground` I have a feeling you're going about this the wrong way. Maybe a little more detail on that, and on actual requirements. – Paul Samsotha Mar 31 '14 at 17:56
  • foreground and ground are Jpanel objects that belong to the custom JLayeredPane. What I require is not different from what google maps looke like. It has to be the logo as the background, the map int the ground and HUD in the foreground. As unloaded sections of the map will allow the logo to be seen. – HuraToTheRescue Apr 01 '14 at 03:29

2 Answers2

1

The most significant issue is you're not calling super.paint, which is preventing what ever was previously painted to the Graphics context from been cleared or any child components from been painted.

For painting the background, you should be using paintComponent, which is used to paint the background of the component.

If you need to paint under the child components, but above the background, you should still use paintComponent, but paint the background first and then the next layer. The components will be painted after paintComponent.

Painting over components is actually more complex

Take a closer look at Custom Painting and Painting in Swing and AWT

Updated based on code snippets

In Screen which extends from Container, you are doing...

@Override
public void paint(Graphics g) {
    super.paint(g);
    GraphicsUtilities.drawPictureTiled(background, g);
    paintComponents(g);
    g.dispose();
}
  • Don't call paintComponents, super.paint has already done this.
  • Don't call dispose on a Graphics context you don't create
  • Based on the rest of the example code I have, you should be extending from JPanel and overriding paintComponent instead. This will allow you to put under the component layer

Because GroundPanel and ForeGroundPanel are both JPanels, there's no need to ever paint them yourself. In fact, you could simply use OverlayLayout or even a GridBagLayout and add them directly to the NestedScreen which is itself a container...

So, I stripped down you example code so I could get it working with the missing code as an example. I got a little more fancy and simply made a JPanel to act as the pause screen

Pony 1Pony 2

This is all done by simply overlaying the components on top of each other using a GridBagLayout

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test1001 {

    public static void main(String[] args) {
        new Test1001();
    }

    public Test1001() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                try {
                    NestedScreen screen = new NestedScreen();
                    screen.setBackgroundLayer(ImageIO.read(getClass().getResource("/Sky.png")));

                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(screen);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public interface GraphicsEngineComponents {

    }

    public class NestedScreen extends Screen implements GraphicsEngineComponents {

        GroundPanel ground;
        ForeGroundPanel foreground;
        private PausePane pausePane;

        public NestedScreen() {

            ground = new GroundPanel();
            foreground = new ForeGroundPanel();
            pausePane = new PausePane();

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;

            add(pausePane, gbc);
            add(foreground, gbc);
            add(ground, gbc);

            MouseAdapter handler = new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    pausePane.setVisible(!pausePane.isVisible());
                }
            };

            addMouseListener(handler);
            foreground.addMouseListener(handler);
            ground.addMouseListener(handler);

        }

        public GroundPanel getGroundLayer() {
            return ground;
        }

        public ForeGroundPanel getForegroundLayer() {
            return foreground;
        }

        public void setBackgroundLayer(BufferedImage background) {
            super.setBackgroundLayer(background);
        }

        public class GroundPanel extends JPanel {

            public GroundPanel() {
                setOpaque(false);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(Color.GREEN);
                g.fillRect(0, getHeight() - 200, getWidth(), 200);
            }

        }

        public class PausePane extends JPanel {

            private JLabel label;

            public PausePane() {
                setVisible(false);
                setOpaque(false);
                setBackground(new Color(0, 0, 0, 128));
                setLayout(new GridBagLayout());

                label = new JLabel("Paused");
                label.setHorizontalAlignment(JLabel.CENTER);
                label.setVerticalAlignment(JLabel.CENTER);
                Font font = label.getFont();
                font = font.deriveFont(Font.BOLD, 48f);
                label.setFont(font);
                label.setForeground(Color.WHITE);
                add(label);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.setColor(getBackground());
                g.fillRect(0, 0, getWidth(), getHeight());
            }

        }

        public class ForeGroundPanel extends JPanel {

            private BufferedImage pony;

            public ForeGroundPanel() {
                setOpaque(false);
                try {
                    pony = ImageIO.read(getClass().getResource("/Pony.png"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (pony != null) {
                    int x = (getWidth() - pony.getWidth()) / 2;
                    int y = getHeight() - 200 - (pony.getHeight() / 2);
                    g.drawImage(pony, x, y, this);
                }
            }

        }
    }

    public class Screen extends JPanel implements GraphicsEngineComponents {

        private BufferedImage background;

        public Screen() {
        }

        @Override
        public String toString() {
            return "Screen{" + "background=" + background + '}';
        }

        public BufferedImage getBackgroundPicture() {
            return background;
        }

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

        protected void setBackgroundLayer(BufferedImage background) {
            if (background != null && background.getHeight() != 0 && background.getWidth() != 0) {
                this.background = background;
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (background != null) {
                g.drawImage(background, 0, 0, this);
            }
        }
    }

}

Take a look at Painting in AWT and Swing and Performing Custom Painting to understand how painting works in Swing.

A basic idea would be to avoid using all these compound or nested components and instead, create an engine that can paint the layers directly onto a Graphics context, maybe even painting to a BufferedImage which you can the paint onto a single component...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Can you please elaborate? I actually do not want to clear previous things as it includes the background. Well I tried calling `super.paint()`. Still does not work. Now I am first drawing the background, then calling super, then calling `paintComponent()` on ground and foreground. Help needed! – HuraToTheRescue Apr 01 '14 at 03:31
  • `Graphics` is shared resource, that means that what ever was painted before your component will still be "present" when you receive it. You MUST clear it first, this is what the paint chain does – MadProgrammer Apr 01 '14 at 04:16
  • So why do I need to clear it when all of it can just be overwritten? Would not that be more resource saving? Or would not? Answer requested. Thanks. – HuraToTheRescue Apr 01 '14 at 06:20
  • Because it meets the expectations of the API – MadProgrammer Apr 01 '14 at 07:11
  • I have added getPrefferedSize() method to return the full positive size and also overrode the isVisible() to return true always. It stil does not work. Help needed! – HuraToTheRescue Apr 28 '14 at 04:23
  • I wouldn't mess with `isVisible` – MadProgrammer Apr 28 '14 at 05:24
  • @HuraToTheRescue An actually [runnable example that demonstrates your problem](https://stackoverflow.com/help/mcve) would involve less guess work and better responses – MadProgrammer Apr 28 '14 at 06:12
  • It was just not drawing so I thought maybe it was because of the inbuilt checks it performs to increase performance like calling `isVisible()` and so I just overrode it. Should I re-ask this question? Because clearly this one is not efficient? – HuraToTheRescue Apr 29 '14 at 16:20
  • The problem is you've not provided a runnable example that demonstrates your problem, so we don't know if you're dining something wrong or not – MadProgrammer Apr 29 '14 at 20:41
  • Well here is the file http://tempsend.com/EE39EAFD90 and here is the test executable http://tempsend.com/59FA138022. Any help is greatly appreciated. Thanks MadProgrammer and everyone else. – HuraToTheRescue Apr 30 '14 at 02:48
  • And what's `Screen` and `GraphicsEngineComponents`? – MadProgrammer Apr 30 '14 at 02:51
  • `GraphicsEngineComponent` is an Empty interface that denotes that the components belong to the base Engine. Here is screen http://tempsend.com/DE8461D250 I thank you for your efforts man. – HuraToTheRescue Apr 30 '14 at 04:55
  • Thanks man. This has worked for me. After such a long time. Seriously, I owe this project to you. – HuraToTheRescue May 01 '14 at 05:18
  • @HuraToTheRescue This is where having the code makes all the difference in been able to find what the problem is ;) – MadProgrammer May 01 '14 at 05:32
  • Seriously man. My bro is gonna be here 3 days later. If you had not helped me, he would have eaten me alive. Marinated and all. Thanks. – HuraToTheRescue May 01 '14 at 05:34
  • You are a genius man. Wish you all of the success in the world.:) – HuraToTheRescue May 01 '14 at 07:47
0

There are a few ways you go about doing this. I'll just introduce one way.

  • Create a background panel where you paint the background image (in the example below, it is BackgroundPanel with the image only being the forresty background). Set that panel as the content pane to the frame.

  • Create another ground panel where you can also paint something (in the example below if it the GroundPanel with only the image of bugs bunny painted.

  • Create your foreground panel and add it to the ground panel. You can add your foreground components to it. (in the example below the foreground image is the grassy hill, and I also add a button to it

  • All the panels' opaque property should be set to false, as to allow the panel behind it to be shown under any transparency.

enter image description here

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class ThreeTier {

    public static final int DIM_WIDTH = 600;
    public static final int DIM_HEIGHT = 400;

    private BufferedImage backgroundImage;
    private BufferedImage groundImage;
    private BufferedImage foregroundImage;

    public ThreeTier() {
        initImages();
        JFrame frame = new JFrame();
        frame.setContentPane(new BackgroundPanel());
        frame.add(new GroundPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void initImages() {
        try {
            backgroundImage = ImageIO.read(getClass().getResource("/resources/background.png"));
            foregroundImage = ImageIO.read(getClass().getResource("/resources/foreground.png"));
            groundImage = ImageIO.read(getClass().getResource("/resources/bugsBunny.png"));
        } catch (IOException ex) {
            Logger.getLogger(ThreeTier.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    class BackgroundPanel extends JPanel {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(backgroundImage, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
        }
    }

    class GroundPanel extends JPanel {

        private static final String RIGHT_ACTION = "rightAction";
        private int imageX = 50;
        private int imageY = 140;

        public GroundPanel() {
            setOpaque(false);
            add(new ForegroundPanel());

            InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke("RIGHT"), RIGHT_ACTION);
            getActionMap().put(RIGHT_ACTION, new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    if (imageX >= DIM_WIDTH) {
                        imageX = 0 - groundImage.getWidth();
                        repaint();
                    } else {
                        imageX += 10;
                        repaint();
                    }

                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(groundImage, imageX, imageY, groundImage.getWidth(), groundImage.getHeight(), this);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
        }
    }

    class ForegroundPanel extends JPanel {

        public ForegroundPanel() {
            setOpaque(false);
            setLayout(new FlowLayout(FlowLayout.TRAILING, 10, 0));
            JButton button = new JButton("I'm in the Foreground!");
            add(button);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(foregroundImage, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(ThreeTier.DIM_WIDTH, ThreeTier.DIM_HEIGHT);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ThreeTier();
            }
        });
    }
}

UPDATE

Of course you could always just use the JLayeredPane. That's what it's for. See this example

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

public class LayeredPaneDemo {

    public static final int DIM_WIDTH = 600;
    public static final int DIM_HEIGHT = 400;

    public LayeredPaneDemo() {
        ContainerPanel container = new ContainerPanel();
        JLabel title = new JLabel("Lame Google Map");
        title.setFont(new Font("verdana", Font.BOLD, 36));
        title.setHorizontalAlignment(JLabel.CENTER);
        JPanel panel = new JPanel(new GridBagLayout());
        panel.add(container);
        JFrame frame = new JFrame();
        frame.add(panel);
        frame.add(title, BorderLayout.NORTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new LayeredPaneDemo();
            }
        });
    }

    public class ContainerPanel extends JPanel {

        public ContainerPanel() {
            JLayeredPane layeredPane = new JLayeredPane();
            layeredPane.setPreferredSize(new Dimension(DIM_WIDTH, DIM_HEIGHT));
            BackgroundPanel bg = new BackgroundPanel();
            GroundPanel gg = new GroundPanel();
            ForegroundPanel fg = new ForegroundPanel();

            bg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
            layeredPane.add(bg, new Integer(1));
            gg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
            layeredPane.add(gg, new Integer(2));
            fg.setBounds(0, 0, DIM_WIDTH, DIM_HEIGHT);
            layeredPane.add(fg, new Integer(3));
            setLayout(new GridBagLayout());
            add(layeredPane);

            setBorder(new LineBorder(Color.BLUE, 10));
        }
    }

    public class ForegroundPanel extends JPanel {

        public ForegroundPanel() {
            JPanel buttonPanel = new JPanel(new GridLayout(3, 3));
            buttonPanel.setOpaque(false);
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("UP"));
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("Left"));
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("Right"));
            buttonPanel.add(new JLabel());
            buttonPanel.add(new JButton("Down"));
            buttonPanel.add(new JLabel());

            FlowLayout flow = (FlowLayout) getLayout();
            flow.setAlignment(FlowLayout.TRAILING);
            flow.setHgap(0);
            flow.setVgap(0);
            add(buttonPanel);
            setOpaque(false);

        }
    }

    public class GroundPanel extends JPanel {

        Image image = null;

        public GroundPanel() {

            try {
                image = ImageIO.read(getClass().getResource("/resources/california.png"));
            } catch (IOException ex) {
                Logger.getLogger(LayeredPaneDemo.class.getName()).log(Level.SEVERE, null, ex);
            }

            setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(image, 0, 0, DIM_WIDTH, DIM_HEIGHT, this);
        }
    }

    public class BackgroundPanel extends JPanel {

        public BackgroundPanel() {
            setLayout(new GridBagLayout());
            setBackground(Color.WHITE);
            try {
                Image img = ImageIO.read(getClass().getResource("/resources/google.jpg"));
                add(new JLabel(new ImageIcon(img)));
            } catch (IOException ex) {
                Logger.getLogger(LayeredPaneDemo.class.getName()).log(Level.SEVERE, null, ex);
            }


        }
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • This will solve my problem, but are you sure it is efficient? But anyways I will try it and let you know it a few hours. Million thanks. – HuraToTheRescue Apr 01 '14 at 04:17
  • Well it partially solves my problem. But the thing is that I need to add components to the ground Level too. Thus if you add components in the `GroundPanel`, your system shall not work as the foreground will be painted along with it's components not after it. I thank you for your effort. – HuraToTheRescue Apr 01 '14 at 07:51
  • The only layer which will just be an Image is the background layer but I have figured it out. Please help me with the foreground things! Help appreciated. – HuraToTheRescue Apr 01 '14 at 07:52