Background Panel
Posted by Rob Camick on October 12, 2008
Many people want to know how to add a backgound image to a frame. The easiest way to do this is to add an image to a JLabel and then add the label to the frame. Is there a better way? Well, the answer depends on your requirements.
The code for this easy approach would be something like:
JLabel contentPane = new JLabel();
contentPane.setIcon( backgroundImage );
contentPane.setLayout( new BorderLayout() );
frame.setContentPane( contentPane );
Why does this work? Well, remember that all Swing components extend Container so you can add child components to them. However, by default, a JLabel uses a null layout so the components don’t appear. By using a BorderLayout the label can now function like a panel and be used as a content pane.
However, this approach has drawbacks:
- a label aways paints the image at its actual size
- the components added to the label must fit in the bounds of the label
So in reality it would probably only be used on fixed size windows where the size of the window is determined by the size of the image.
In most other cases we need a more flexible approach, so we need a more flexible component. Introducing, BackgroundPanel, an extension of JPanel that provides some custom painting support for the drawing of images. The basic support provided by this class allows the images to be painted in one of 3 styles:
- scaled image (the default)
- tiled images
- actual sized image – the position of the image is controlled by specifying the horizontal and vertical alignment (center alignment is the default)
In addition to drawing images, this component can also be used to paint backgrounds that are not a solid colour. The Java API conveniently provides a Paint interface. A new setPaint(…) method was added to specify a Paint object to be used by the custom painting code. The Paint object can be used without an image. When both an image and a Paint object are used, the order of painting will be:
- paint the background
- paint using the Paint object
- draw the image
Also, the add(…) methods have been overridden to make each component added to the panel non-opaque. After all, if you are going to all the trouble to create a fancy background you dont want your opaque panel to block the custom painting. For components that use a renderer the renderer will also need to be transparent. For example, on a JTable, you can use the following to set the alpha value of the Color to 0, which results in a transparent background colour:
table.setBackground( new Color(0, 0, 0, 0) );
Following is the the code used to create the panel displayed in the attached image:
BackgroundPanel panel = new BackgroundPanel(duke, BackgroundPanel.ACTUAL, 1.0f, 0.5f);
GradientPaint paint = new GradientPaint(0, 0, Color.BLUE, 600, 0, Color.RED);
panel.setPaint(paint);
If the above class is too complex for your requirement then you can just do basic painting of the image on the panel. A BasicBackgroundPanel class might look something like:
public class BasicBackgroundPanel extends JPanel
{
private Image background;
public BasicBackgroundPanel(Image background)
{
this.background = background;
setLayout( new BorderLayout() );
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(background, 0, 0, null); // image full size
//g.drawImage(background, 0, 0, getWidth(), getHeight(), null); // image scaled
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(background.getWidth(this), background.getHeight(this));
}
}
Arasdasd said
THIS DID NOT WORK
WASTE OF TIME
Rob Camick said
Not a very helpfull comment. Of course it works or I wouldn’t take the time to write the blog entry or post the code. It may not work the way you want it to or expect it to, but that does not make it wrong, only different. If you have a specific problem you can always sent your “demo” code by using the “Contact Us” link.
Greg Blass said
Great post here. You would not believe how many other links I looked at before I was able to use the simple 4 lines of code you posted to add a background image to a JFrame. Thanks Rob.
Rob Camick said
Thanks for the feedback. Glad your searching lead you to an answer.
Eli said
Worked for me =)
BackgroundPanel bgPanel = new BackgroundPanel(img, BackgroundPanel.SCALED, 0.0f, 0.0f);
SCALED is so awesome, thank you! Fits the image, however you resize the frame.
Garratt said
Hey Rob, This seems like it would be perfect for what I’m trying to achieve (scaled image) but I’m having trouble. I’m wondering why use ‘Image’. From what I can tell that means I need to make another class that extends Image and defines its methods. Typically for images I use ImageIcon as it can be easily instantiated but ‘Image’ is an abstract class and therefore cannot be instantiated.
JLabel.setIcon(new ImageIcon("resources/img.png");
Not sure how to make this class work from the example given.Can you please explain how I create and use the default method for the class?
Rob Camick said
There is no need to use an ImageIcon to read in an Image. You can use ImageIO to read the Image. But if you really want to use an ImageIcon you can use the getImage() method to get the actual Image.
James Wars said
The BackgroundPanel does not show the image when multiples panels and a frame are used. I have a structure that starts at the top level with a JFrame ‘frame1’ then a JPanel ‘MainPanel’—(CardLayout structure) and inside the MainPanel, i have another JPanel ‘StudentPanel’ that shows yet another small sized JPanel ‘ImagePanel’ that finally add as a component the BackgroundPanel and sets it’s BorderLayout.CENTER?
Does this mean i have to repaint and revalidate every single one of these panels?
Rob Camick said
You don’t add the BackgroundPane to your ImagePanel
You add the Image to the BackgroundPanel. Then you can add other components to the BackgroundPanel if your wish.
If you need more help then post a proper SSCCE that demonstrates the problem. If you don’t know what a SSCCE is then search the web.
Rob
Anonymous said
Nice post. It works great and is easy to use. Love it! Thanks for all the effort that went into this.
Rob Camick said
Glad you found it easy to use.
gadget00 said
I’ve been trying to use this on a JDesktopPane, which is on top of a JFrame but so far I haven’t been able to roll it. Will it work?
Rob Camick said
I don’t think you can use this panel directly on a JDesktopPane because it is designed to drag components around the pane. However, you should be able to override the paintComponent() method of your desktop panel to use the painting code found in this class.
Mudassir said
Loved this one….You solved my biggest trouble !
Thanks a million rob !
Rob Camick said
Glad your problems are solved.
Ayaaz said
Hey but there is one small glitch, I don’t know if that’s from my side or not… Well,the thing is when I run my code, my frame is visible with out the background image, & until I re size/minimize/maximize the frame, background doesn’t show up, why is it so ?
Rob Camick said
If a component doesn’t paint until you resize the frame that is usually an indication that you added the component to the frame AFTER the frame was made visible. All components should be added to the frame BEFORE you make the frame visible.
Ayaaz said
One more thing is….I think I am unable to use it on other lay-outs, except for the default layout of java swing, can u put some light on it please ?….Thanks :)
Rob Camick said
I updated the source code to provide a default “preferred size” equal to the size of the image being used.
Richard said
This was a lifesaver and easy to implement! Like it so much that this is my first posting to code samples…
Thank you!!!
Richard
Richard said
Also – for folks new to Java (like me)… download the ‘BackgroundPanel.java’ file and drag it into the package that contains your JFrame. There’s probably other ways, but that worked for me.
William said
You sir are a true gentleman and a scholar, thank you very much for sharing this class!
Rob Camick said
Thanks for the support.
Paul said
Some working code…
Nikos said
Thank you,
this saved me a lot of time..
In my case I wanted to add a JScrollPane with a JtextPane on a “tiled” BackgroundPanel ,but I didn’t want the background of the JTextPane to change so I just had to erase the “if” block in the “makeComponentTransparent” and everything worked fine.
Rob Camick said
Glad it saved time. Instead of customizing the code you could have used the setTransparentAdd(…) method. Set it to false, add the component and then reset to true.
sunil shahi said
Thank you very much however I need 1 more help from you.. I used Paul’s code above as a demo to understand. Of course it works but I need to give the whole path of the image like “C:\\Users\\Owner\\Documents\\NetBeansProjects\\FetchMail\\src\\myPackage\\BackGround.PNG” to make it work. if I do ./myPackage/BackGround.PNG it doesnot work. Can you help me where I am doing the mistake?
Boyce said
I have the very same problem.
Bruno Pinheiro said
Worked very well , tanks !!
Sarang said
Thank you sir, this code made my problem solve in minutes…. thanx a lot…. :)
bohalloran95 said
You have no idea how much aggravation this has saved me! Thank you!
Anarcho-Willi said
This class was indeed a great time saver for me as well. I added two different types of displaying the background images (horizontally and vertically scaled):
red said
Thanks for this great component! I think that there’s a little problem. I’ve a BagrkoundPanel p1 which renders correctly his image. p1 has a smaller child JPanel which should render a background color: in fact it doesn’t! I think that the problem is related to paintComponent… maybe at the end of method, a revalidate for all children must be invoked?
red said
I think I’ve found a solution… there’s a property transaprentAdd which solves perfectly my problem! Great work!
Martijn Verburg (@karianna) said
This is awesome – thanks Rob!
Rob Camick said
Thanks for the feedback.
K said
When I tried using this, I noticed it gobbling up all of my CPU and discovered that the repaint() at the end of paint_component() puts it into an infinite loop. Commenting out that call fixed the problem and everything still works fine.
Rob Camick said
This class does not use the repaint() method in the paintComponent() method. You must have modified your version. Maybe try downloading the code again to make sure you have the original version.
ADreaming said
Second semester CSCI minor here. I’m using this for my final homework assignment, and I love it! Thanks for this awesome code!
Josh said
Thanks Rob! This works perfectly!
Sam said
Awesome!! Works perfectly :D Thanks for sharing
bansidhe said
Yowza! So easy, and works perfectly as far as I can tell! I love it. Thanks!
Rob Camick said
Glad to see it does what you want.
Oscar Hernández said
Thank you very much Mr. Camick, this did the trick for me.
However, at first it “didnt work” as some people here have mentioned but after a bit of fiddling through the code I found out that it is because if you add more elements to the elements that are put inside the BackgroundPanel you have to set those to be transparent manually. Dont know if this is something that can be fixed in the actual class code though.
Otherwise I really like it, thanks!
Rob Camick said
That was a design decision that I made. I decided that only components directly added to the panel should be automatically made non-opaque. If you want everything that you add to the panel then you could customize the current code to do a recursive loop through all the components found on the panel being added.
Anonymous said
Thanks Rob. This works awesome.
Deeps said
Hi, if you set an image over a JLabel and set it in contentpane of JFrame… the how would you add other components to the same frame?
Bruce said
Your example was easy to understand, quickly educated me on what I wanted to know, and was simple to test and adapt. Thanks !
Ben said
Hi, is there any way to make this work for an animated gif?
Rob Camick said
Not that I know of. You would need to use the first suggestion which is to just use the JLabel as the background and then set an appropriate layout manager.
Paul said
This worked well for me, thanks. I have some issue extending some custom classes, but thats my problem to work out.
Eddie said
Help please, I have tried this code but it seems to do nothing. What am I doing wrong?
I only tried it on a sample code so it’s straightforward. My code:
Rob Camick said
You need to add all the components to the frame BEFORE you make the frame visible. This is a Swing requirement and has nothing to do with this class. The other possible problem is that your code can’t find the image. Typically the image would be in the same directory as the class file so you don’t need the “c:\\”. Try using ImageIO to read the image. You will get an error message if the file is not found.
Anonymous said
Thanks for the help man, i tried all the other methods for placing a background on a JPanel but this has to be the simplest way to do it.
TL said
Awesome code it worked like a charm for me. I had a problem with my background not scaling properly with my JTabbedPane on my JFrame with a background image but this seems to have fixed that problem!
I love English !!!! said
You save my day. But, I have a question: How can I add other components into it when I set background as a JLabel?
Rob Camick said
I don’t understand the question. The label becomes the content pane, so you add components to the frame normally. That is why you set the BorderLayout on the label.
Griefed said
This is an absolute BEAST of a class. I was bashing my head through my keyboard trying to use an image as the background for a JFrame, which would then hold a JTabbedPane. This allowed me to beautifully add a tiled image as the background.
Thank you so much for this!