junkie
junkie

Reputation: 829

Why is paintComponent never called?

I have the following code. Basically I have a frame which has a background image. I also have three panels within the frame: panels 1, 2 and 3. 2 & 3 work fine as I haven't subclassed them. However, panel 1 as soon as I subclassed it i.e. put the logic inside the paintComponent method of JPanel stopped working as that method is never called and foo is never printed. I'm not able to figure out why. Would appreciate your help. I've tried a few suggestions from other similar threads and they haven't helped.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) throws IOException {

        JFrame.setDefaultLookAndFeelDecorated(true);
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

        int fooPanelX = 5;
        int fooPanelY = 160;
        int fooPanelWidth = 470;
        int fooPanelHeight = 305;

        int bar0PanelX = 5;
        int bar0PanelY = 550;
        int bar0PanelWidth = 230;
        int bar0PanelHeight = 210;

        int bar1PanelX = bar0PanelX * 2 + bar0PanelWidth + bar0PanelX;
        int bar1PanelY = bar0PanelY;
        int bar1PanelWidth = bar0PanelWidth;
        int bar1PanelHeight = bar0PanelHeight;

        JPanel panel1 = new Panel1(fooPanelX, fooPanelY, fooPanelWidth, fooPanelHeight);

        JPanel panel2 = new JPanel();
        panel2.setBackground(Color.WHITE);
        panel2.setLocation(bar0PanelX, bar0PanelY);
        panel2.setSize(bar0PanelWidth, bar0PanelHeight);
        panel2.setOpaque(false);
        panel2.setBorder(BorderFactory.createLineBorder(Color.WHITE));
        panel2.setBounds(bar0PanelX, bar0PanelY, bar0PanelWidth, bar0PanelHeight);

        JPanel panel3 = new JPanel();
        panel3.setBackground(Color.WHITE);
        panel3.setLocation(bar1PanelX, bar1PanelX);
        panel3.setSize(bar1PanelWidth, bar1PanelHeight);
        panel3.setOpaque(false);
        panel3.setBorder(BorderFactory.createLineBorder(Color.WHITE));
        panel3.setBounds(bar1PanelX, bar1PanelY, bar1PanelWidth, bar1PanelHeight);

        JLabel imagePanel = new JLabel(new ImageIcon(ImageIO.read(new File("image.png"))));

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == 27) {
                    System.exit(0);
                }
            }

        });

        frame.setContentPane(imagePanel);
        frame.getContentPane().add(panel1);
        frame.getContentPane().add(panel2);
        frame.getContentPane().add(panel3);
        frame.setLocation((int) (screenSize.getWidth() * 0.75),
                (int) (screenSize.getHeight() * 0.25));
        frame.pack();
        frame.setVisible(true);

    }

    @SuppressWarnings("serial")
    static class Panel1 extends JPanel {

        int x, y, w, h;

        public Panel1(int x, int y, int w, int h) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
        }

        @Override
        public void paintComponent(Graphics graphics) {

            System.out.println("foo");
            super.paintComponents(graphics);

            setBackground(Color.WHITE);
            setLocation(x, y);
            setSize(w, h);
            setOpaque(true);
            setBorder(BorderFactory.createLineBorder(Color.WHITE));

         }

    }

}

Update: If you aren't able to find the issue then please could you provide me with an alternative way of doing the following. I need a frame with a background image and three panels on top of the background image. The three panels have to have pixel perfect locations and sizes on the background image to look right. That's it pretty much. I'll be repainting the three panels but the background image will remain the same.

Upvotes: 3

Views: 1139

Answers (2)

junkie
junkie

Reputation: 829

I decided to tackle the problem in a very different way. This is how I did it. I startedc completely from scratch with my code. I created a JFrame instance and a Canvas instance (the canvas was subclassed). In the canvas I used drawImage() to apply the background image. Then for each of the three areas that I wanted to animate on the background image, instead of creating three JPanels, I simply used fillRect() within the canvas to fill the right areas on the image. That's it. Nice and simple. The repaint() every second does flickr on the three areas and that's the next challenge. I'm guessing I have to use double buffering but it's not something I've used before so I'll look into that next. Anyway, using a single canvas in place of three JPanels proved a heck of a lot simpler and the reason I was able to do that was because the background image provided everything else visually. All I had to do was drawImage() and fillRect(). Thanks for all contributions.

Update: I have now completed this task. There was one thing I changed about the above. While attempting to double buffer with Canvas I had a few issues: the usual "component must have valid peer" exception. While looking into that I learnt that one should not use Canvas in Swing and that the practice of using it was mixing AWT and Swing. So I swapped it out for JComponent (as I didn't need anything that JPanel offered). And as Swing is double buffered by default my work was complete. No flicker and simplified code.

Upvotes: 2

Guillaume Polet
Guillaume Polet

Reputation: 47608

Well, the problem is that you are not using an appropriate LayoutManager.

JLabel does not come with any LayoutManager by default. So when you add your Panel1, it has a size of 0x0 and is located in (0,0) and since no LayoutManager will change that, it will keep that size and location. With empty bounds, your component is never painted, hence your paintComponent method is never called.

Now, you should NEVER do this in paintComponent:

        setBackground(Color.WHITE);
        setLocation(x, y);
        setSize(w, h);
        setOpaque(true);
        setBorder(BorderFactory.createLineBorder(Color.WHITE));

Do that in the constructor or some other method. paintComponent is meant for "painting a component", not changing its properties.

Upvotes: 2

Related Questions