JPanel background image doesn't apply to a JPanel inside it - java

I'm working on a simple registration window that appears when the java app opens.
It's a JFrame, that has a JPanel in it, which has text fields, labels, and another panel which also contains text fields and labels.
My problem is that the outside panel has a background image, but it doesn't apply to the panel inside it as seen here:
Here is the whole window code:
public void start() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame = new JFrame("Chat");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//frame.setMaximumSize(new Dimension((int)screenSize.getWidth()-1000, (int)screenSize.getHeight()-1000));
frame.setMinimumSize(new Dimension((int)screenSize.getWidth()/2-200,(int) screenSize.getHeight()/2));
frame.setResizable(false);
welcome = new LoginPanel();
welcome.setLayout(new BoxLayout(welcome, BoxLayout.Y_AXIS));
welcome.setBorder(BorderFactory.createEmptyBorder(50, welcome.getWidth()/2-500, 50, welcome.getWidth()/2 -500));
//repaintThread = new Thread(new RepaintRunnable(frame, welcome));
//repaintThread.start();
request = new JLabel("Please fill the required fields below:");
request.setFont(new Font("Serif", Font.BOLD, 20));
request.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
request.setAlignmentX(Component.CENTER_ALIGNMENT);
userInfo = new JPanel();
userInfo.setLayout(new BoxLayout(userInfo, BoxLayout.Y_AXIS));
userInfo.setAlignmentX(Component.CENTER_ALIGNMENT);
Font fieldType = new Font("Serif", Font.PLAIN, 15);
PlainDocument doc = new PlainDocument();
doc.setDocumentFilter(new NameDocument());
enterFirstName = new JLabel("First name:");
enterFirstName.setAlignmentX(Component.LEFT_ALIGNMENT);
enterFirstName.setFont(fieldType);
firstName = new JTextField(20);
firstName.setMaximumSize(firstName.getPreferredSize());
firstName.setDocument(NameDocument.getNewNameDocument(NameDocument.NO_SPACE));
firstName.getDocument().addDocumentListener(new ChangeDocumentListener());
firstName.addActionListener(new ConfirmListener());
firstName.setAlignmentX(Component.LEFT_ALIGNMENT);
enterSecName= new JLabel("Surname:");
enterSecName.setAlignmentX(Component.LEFT_ALIGNMENT);
enterSecName.setFont(fieldType);
secName = new JTextField(30);
secName.setMaximumSize(secName.getPreferredSize());
secName.setDocument(NameDocument.getNewNameDocument(NameDocument.HAS_SPACE));
secName.getDocument().addDocumentListener(new ChangeDocumentListener());
secName.addActionListener(new ConfirmListener());
secName.setAlignmentX(Component.LEFT_ALIGNMENT);
enterNickname = new JLabel("Nickname (how other people will see you in chat):");
enterNickname.setAlignmentX(Component.LEFT_ALIGNMENT);
enterNickname.setFont(fieldType);
nickname = new JTextField(30);
nickname.setMaximumSize(nickname.getPreferredSize());
nickname.setDocument(NameDocument.getNewNameDocument(NameDocument.NO_SPACE));
nickname.addActionListener(new ConfirmListener());
nickname.setAlignmentX(Component.LEFT_ALIGNMENT);
userInfo.add(enterFirstName);
userInfo.add(firstName);
userInfo.add(enterSecName);
userInfo.add(secName);
userInfo.add(enterNickname);
userInfo.add(nickname);
confirm = new JButton("Submit");
confirm.setAlignmentX(Component.CENTER_ALIGNMENT);
confirm.setEnabled(false);
confirm.addActionListener(new ConfirmListener());
welcome.add(request);
welcome.add(userInfo);
welcome.add(new Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10)));
welcome.add(confirm);
frame.getContentPane().add(BorderLayout.CENTER, welcome);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
And here is the LoginPanel code(the outside JPanel):
public class LoginPanel extends JPanel {
public void paintComponent(Graphics g) {
try {
super.paintComponent(g);
BufferedImage background = ImageIO.read(new File("Background.jpg"));
g.drawImage(background, 0, 0, getWidth(), getHeight(), null);
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
it'll also be great if someone will give me advice on how to make this code better, since I'm beginner in Java.

Remember to call setOpaque(false); on any inner JPanels (and on some other components -- though not all) that cover up your image-displaying JPanel. This will allow background images to show through. You don't have to do this with JLabels as they are see-through (non-opaque) by default, but you do with JPanels.
So for you it will be:
userInfo = new JPanel();
userInfo.setOpaque(false);
One other problem is that you should never read in images from within a paintComponent method. This method may be called often, and why re-read the image when it can and should be read in only once. And more importantly, this method should be fast as possible since slowing it down needlessly will slow down the perceived responsiveness of your program. Read the image in once, and store it in a variable that is then displayed within paintComponent.
e.g.,
public class LoginPanel extends JPanel {
private BufferedImage background;
public LoginPanel(BufferedImage background) {
this.background = background;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
}
}
}
Read the image in and then pass it into the constructor of your LoginPanel class.

Not related to your problem but:
frame.getContentPane().add(BorderLayout.CENTER, welcome);
Since JDK 4 you don't need to use the getContentPane() method you can just use frame.add(...) and the component will be added to the content pane.
Also you are using the wrong add(...) method. You are using add(constraint, component). If you read the API for the method it will tell you to use the add(component, constraint) method.
So you could be using:
frame.add(welcome, BorderLayout.CENTER);

Related

How to overlap a JPanel(background) and a JTextField

I am creating a log in application for someones graduation, I need several text fields and a background, I have added the background and now need to add the text fields, the problem is that they won't seem to go on top of each other.
I have tried them each separately and without one another they both work perfectly but i can't get them to stack, I have seen several answers on this site to deal with a similar problem but for this application I need to put several text fields on the background as apposed to just one, here is what I have thus far...
//creates the frame with a title as a parameter
JFrame frame = new JFrame("Sign In Sheet");
//sets the size
frame.setSize(1000, 556);
//makes it so the application stops running when you close it
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//puts it in the center of the screen
frame.setLocationRelativeTo(null);
//makes it so you can't resize it
frame.setResizable(false);
//setting the background by looking for the image
try{
frame.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("C:/Users/Gabriel R. Warner/Desktop/clouds.png")))));
}catch(IOException e){
//and prints an error message if it's not found
System.out.println("well it didn't work");
}
//adding text fields with names apropriate to function
JTextField name1 = new JTextField();
name1.setPreferredSize(new Dimension(200, 15));
name1.setBackground(Color.WHITE);
frame.add(name1);
//makes frame visible
frame.setVisible(true);
Simply stated the text field won't show up with the background and all the results only offer answers for a single text field
The problem is in this line: frame.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("C:/Users/Gabriel R. Warner/Desktop/clouds.png")))));
In this line you set a JLabel as the content pane of your JFrame. Then, you frame.add(name1); So you are adding a JTextField to a JLabel...Well this does not seem right, right?
The answer would be to create a new JPanel, add the background image to this panel, set the panel as the content pane of the frame and finally add the textfield to the panel/contentpane.
An example:
#SuppressWarnings("serial")
public class FrameWithBackgroundImage extends JFrame {
public FrameWithBackgroundImage() {
super("Sign In Sheet");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
try {
Image bgImage = loadBackgroundImage();
JPanel backgroundImagePanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bgImage, 0, 0, null);
}
};
setContentPane(backgroundImagePanel);
} catch (IOException e) {
e.printStackTrace();
}
JTextField textField = new JTextField(10);
add(textField);
}
private Image loadBackgroundImage() throws IOException {
File desktop = new File(System.getProperty("user.home"), "Desktop");
File image = new File(desktop, "img.jpg");
return ImageIO.read(image);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new FrameWithBackgroundImage().setVisible(true);
});
}
}
Preview:
Worth to read question: Simplest way to set image as JPanel background

JPanel location issues

I have to create a GUI and I'd really like to set a background to it.
In order to do this, I've created a class called "Backround" where I'm using the paintComponent method. I gave it the file I want to set on the background and it worked.
Here is my Background class :
public class Background extends JPanel
{
public void paintComponent(Graphics g)
{
try {
Image img = ImageIO.read(new File("./fond.jpg"));
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
However, once the image is on the background, the panels that were on my frame are not anymore in the same location and I've really no idea how to fix that and still haven't found any relevant topics about this.
Here is a quote of my class where I'm describing the GUI :
this.setContentPane(new Background());
this.setTitle("Arena");
this.setSize(800, 500);
this.setLocationRelativeTo(null);
//this.setLayout(new FlowLayout(FlowLayout.CENTER));
//this.setLayout(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
// ******************************************** PANEL 1 ******************************************
Panel P1 = new Panel();
this.add("NORTH", P1);
P1.setLayout(new FlowLayout());
P1.add(new Label ("Joueur 1"));
P1.add(new Label ("Action"));
P1.add(new Label ("Joueur 2"));
//P1.setVisible(true);
// ********************************************* PANEL 2 ******************************************
Panel P2 = new Panel();
P2.setLayout(new FlowLayout());
P2.add(Liste1);
// Boutons
Button B1 = new Button("FIGHT");
P2.add(B1);
Button B2 = new Button("HEAL");
P2.add(B2);
P2.add(Liste2);
this.add("WEST", P2);
// P2.setVisible(true);
Furthermore, when I remove the call to my Background constructor, the panels are getting back to their inital locations.
I hope you could help me or redirect me!
Thanks!
Antoine Sbert
Make sure you call super.paintComponent before performing any customer painting
Don't load resources (like images) from with in the paint method, this will have an impact on the performance of you program
JPanel by default use a FlowLayout, but you never change the layout after you apply the Background panel as the contentPane. After calling setContentPane, use setLayout(new BorderLayout()) or apply the layout in the classes constructor (at the same time when you load the image)
Make sure you call setVisible last, after you've established the basic UI

Gradient background in a JFrame

I have googled this and read a lot but did not find an answer that suits my needs, so I'll ask here:
I would like to have a gradient background in my JFrame. Currently the background is a single colour. My code looks something like this:
//imports
public class Game {
//some other irrelevant instance variables
JFrame frame = new JFrame("Game");
public Game() {
frame.getContentPane().setBackground(new Color(200,220,200));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setPreferredSize(new Dimension(frameX,frameY)); //frameX and frameY are instance variables
getMenu(); //method that adds a few JLabels to the JFrame and so on
}
}
The methods that I have read about apply to classes that extend JPanel or JFrame (and then use GradientPaint or something like that), but as you can see I use JFrame as an instance variable. Can someone help me out?
Edit: Picture:
Now, obviously, your example image above does not specify buttons and does not add a label for the message at the bottom. But since it was obvious you intended the user to select those options, I used buttons. The label at the bottom is just to show proof they are buttons (with an action listener attached, to show the message).
The advantage of using actual buttons is that they are also keyboard accessible (press Enter to see the first message, press Tab to navigate to the next one...
If the game does not need to be keyboard accessible, you can swap those out for labels and add a mouse listener. I'll leave that to you.
The code has a lot of comments containing the word 'adjust'. Look at them closely, check the JavaDocs, adjust them as needed..
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class GradientPaintBackground {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
JPanel gui = new JPanel(new BorderLayout(15, 15)) {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Point point1 = new Point(10, 10);
Point point2 = new Point(
getWidth() - 10,
getHeight() - 10);
final GradientPaint gp = new GradientPaint(
point1, Color.YELLOW,
point2, new Color(255, 225, 100),
true);
// we need a Graphics2D to use GradientPaint.
// If this is Swing, it should be one..
final Graphics2D g2 = (Graphics2D) g;
g2.setPaint(gp);
g.fillRect(0, 0, getWidth(), getHeight());
}
};
// adjust size to need.
gui.setBorder(new EmptyBorder(20, 20, 20, 20));
// Start: Add components
// adjust size to size of logo
BufferedImage logo = new BufferedImage(
100, 40, BufferedImage.TYPE_INT_RGB);
JLabel logoLabel = new JLabel(new ImageIcon(logo));
gui.add(logoLabel, BorderLayout.NORTH);
// adjust spacing to need
JPanel menuPanel = new JPanel(new GridLayout(0, 1, 20, 20));
menuPanel.setBorder(new EmptyBorder(5, 55, 5, 5));
// allow the BG to show through..
menuPanel.setOpaque(false);
gui.add(menuPanel);
String[] actionTexts = new String[]{
"Play Game", "Tutorial", "Other"
};
final JLabel messages = new JLabel("Ready to play? "
+ "Select an option");
gui.add( messages, BorderLayout.PAGE_END );
ActionListener al = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JButton) {
JButton b = (JButton)e.getSource();
messages.setText(b.getText() + " selected!");
}
}
};
for (int ii = 0; ii < actionTexts.length; ii++) {
JButton b = new JButton(actionTexts[ii]);
b.setContentAreaFilled(false);
b.setHorizontalAlignment(SwingConstants.LEADING);
b.setBorder(null);
b.addActionListener(al);
menuPanel.add(b);
}
// End: Add components
JFrame f = new JFrame("Gradient Background in JFrame");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See https://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
f.setMinimumSize(f.getSize());
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
General Tip
Java GUIs might have to work on a number of platforms, on different screen resolutions & using different PLAFs. As such they are not conducive to exact placement of components. This is why you are continually seeing the types of problems you see. Toss layouts out the window, and all hell breaks loose.
To organize the components for a robust GUI, instead use layout managers, or combinations of them1, along with layout padding & borders for white space2.

Adding JScrollPane to JPanel

I'm trying to add a JScrollPane to an JPanel from a separate class. And thanks to some questions, which were asked so far, I could help myself create them. But my problem is still a little bit special.
I want to display an image on a JPanel and if the image is to large for the panel, I want to add scrollbars. But the scrollbars won't appear.
(When I set the ScrollPaneConstants to ****_SCROLLBAR_ALWAYS the frame of the bar appears, but without the bars to scroll).
I guess i have to connect the imagesize with the bars, so that they appear?
Some pieces of my code:
MainWindow
public class Deconvolutioner extends JFrame
{
Draw z;
Picturearea picturearea;
class Draw extends JPanel
{
public void paint(Graphics g)
{
}
}
public Deconvolutioner()
{
setTitle("Deconvolutioner");
setLocation(30,1);
setSize(1300,730);
super.setFont(new Font("Arial",Font.BOLD,11));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
FlowLayout flow = new FlowLayout(FlowLayout.CENTER);
this.setLayout(flow);
picturearea = new Picturearea();
picturearea.setLayout(new GridBagLayout());
JScrollPane scrollPane = new JScrollPane(picturearea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(1000, 664));
getContentPane().add(scrollPane, flow); // add scrollpane to frame
add(z = new Draw());
setVisible(true);
}
}
JPanel Class
public class Picturearea extends JPanel
{
BufferedImage image;
int panelWidth, panelHeight, imageWidth, imageHeight;
public Picturearea()
{
setBackground(new Color(210,210,210));
setBorder(LineBorder.createBlackLineBorder());
setVisible(true);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
public void setPicture(BufferedImage picture)
{
try
{
image = picture;
}
catch (Exception e)
{
System.err.println("Some IOException accured (did you set the right path?): ");
System.err.println(e.getMessage());
}
repaint();
}
}
Thanks for your time.
The problem is that the JScrollPane has no way to know if it should display scroll bars or not, since the Picturearea it contains doesn't tell anything about its preferred size (or rather, it returns the preferred size based on its layout and on the components it contains. But since it doesn't contain any component, the returned preferred size is probably (0, 0)).
I would simply use a JLabel instead of the custom Picturearea class. A JLabel can display an image just fine, and it returns the appropriate Dimension when asked for its preferred size.
You can create a JLabel first and then add the Label to JPanel picturearea before creating instance for JScrollPane .
Have a try and it will work.
Example code is as follows:
JLabel imageLabel = new JLabel(new ImageIcon("d:\\099.jpg"));
picturearea.add(imageLabel);**
JScrollPane scrollPane = new JScrollPane(picturearea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

JButton only appears on mouse over?

Here is my code: I took out some stuff that I felt wasn't necessary. I might've took out some brackets too, but I'm just trying to show the content I have.
What happens is, when I run the program, the background image paints (it's a PNG in resources), and only ONE button appears (my PLAY button), which is the first button - it's auto-selected.
I actually have four buttons but I've only included PLAY and INSTRUCTIONS in my code. The other three don't show up unless I mouse over them. I know it's probably something weird with the paint method, but I don't know how to fix it.
If I select a different button and minimize the window then open it again, that selected button is the only one that appears. I have to mouse over to get the other buttons to appear.
I've added super.paint() to the paint method too and I get all my buttons but the background is grey.
I think the problem is super.paint() paints all my buttons, and g.drawImage(bg, 0, 0, null) only paints my background and I can't do one without painting over the other.
Sorry if this was a mess. I'm new at Java and I have trouble articulating what I'm trying to say.
public class MainMenu extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
//variables
public static Image bg;
public static void main(String[] args) {
MainMenu mainFrame = new MainMenu();
mainFrame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
mainFrame.setResizable(false);
mainFrame.setLocationRelativeTo(null);
mainFrame.setTitle ("Zumby");
mainFrame.setLayout(null);
// Loads the background image and stores in bg object.
try {
bg = ImageIO.read(new File("zumby.png"));
} catch (IOException e) {
}
mainFrame.setVisible(true);
}
/**
* Overrides the paint method.
* MONDAY
*/
public void paint(Graphics g)
{
// Draws the img to the BackgroundPanel.
System.out.println("paint");
g.drawImage(bg, 0, 0, null);
}
/**
*/
public MainMenu() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setOpaque(false);
setContentPane(contentPane);
contentPane.setLayout(null);
//create buttons
JButton btnPlay = new JButton("PLAY");
btnPlay.setBackground(Color.BLACK);
btnPlay.setForeground(Color.WHITE);
btnPlay.setFont(font);
btnPlay.setBorder(border);
btnPlay.setFocusPainted(false);
//if "Play" is clicked
btnPlay.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent click) {
setVisible(false);
new GamePlay(); //opens up GamePlay window
}
});
btnPlay.setBounds(600, 64, 141, 61);
contentPane.add(btnPlay);
JButton btnInstructions = new JButton("INSTRUCTIONS");
btnInstructions.setBounds(600, 160, 141, 61);
btnInstructions.setBackground(Color.BLACK);
btnInstructions.setFocusPainted(false);
// btnInstructions.setEnabled(true);
contentPane.add(btnInstructions);
repaint();
pack();
setVisible(true);
}
}
Swing uses a "layering" concept for it's painting...
paint calls paintComponent, paintBorder and paintChildren. By overriding paint and failing to call super.paint, you've prevented the component from painting it's various layers.
In Swing, it is preferred to use paintComponent to provide custom painting, which allows you to paint underneath any other components that might be added to the component.
public class TestPaint01 {
public static void main(String[] args) {
new TestPaint01();
}
public TestPaint01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Image backgroundImage;
public TestPane() {
try {
BufferedImage background = ImageIO.read(new File("/path/to/image.jpg"));
//backgroundImage = background.getScaledInstance(-1, background.getHeight() / 4, Image.SCALE_SMOOTH);
backgroundImage = background;
} catch (IOException ex) {
ex.printStackTrace();
}
setLayout(new GridBagLayout());
add(new JButton("Hello"));
}
#Override
public Dimension getPreferredSize() {
return backgroundImage == null ? super.getPreferredSize() : new Dimension(backgroundImage.getWidth(this), backgroundImage.getHeight(this));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (getWidth() - backgroundImage.getWidth(this)) / 2;
int y = (getHeight() - backgroundImage.getHeight(this)) / 2;
g.drawImage(backgroundImage, x, y, this);
}
}
}
You might find A Closer look at the Paint Mechanism and Painting in AWT and Swing informative.
I think it's because you're overriding the paint method. It's better to override repaint, then call super.repaint(); Like this:
public void repaint(Graphics g)
{
super.repaint(g);
// Draws the img to the BackgroundPanel.
System.out.println("paint");
g.drawImage(bg, 0, 0, null);
}
Then the components get redrawn as well.
But if all you want to do is show an image as the background see here.
You're overriding paint() but don't call super.paint(). So the normal painting of the components done by the JFrame's paint() method implementation is not executed.
Since you are using Swing and JFrame the painting mechanism used to override paintComponent not paint that is usually used with applets or AWT.

Categories