edit// my question is simpler than the other one so please just answer here. the other question looks too complicated for me to understand.
I want to add an image to a panel, but not sure how it's done. I don't want to do it from the design page because I didn't Design my panel I only coded it to show up. so does anyone know what code I need to add for an image to show up on there? and where do I save the image so that it can be included. here is the code I've done so far
JFrame frame = new JFrame("JButton");
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,200);
JPanel panel = new JPanel();
frame.add(panel);
JButton button = new JButton("London");
panel.add(button);
JLabel label = new JLabel("Click", JLabel.CENTER);
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel{
private BufferedImage image;
public ImagePanel() {
try {
image = ImageIO.read(new File("image name and path"));
} catch (IOException ex) {
// handle exception...
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters
}
}
All you need to do is,
Read image file.
Draw image to background with help of Graphics object.
just replace JPanel panel = new JPanel(); with below code.
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Image image = null;
try {
image = ImageIO.read(new URL("https://www.google.co.in/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png"));
} catch (IOException e) {
e.printStackTrace();
}
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
Alright, there are 2 ways to add your image:
Using custom painting by overriding JPanel#paintComponent(...) method.
Using a JLabel to display the image and applying to it various layout managers to get the desired GUI.
I'm going to expand on how to use the 1st way with some comments in the code, the original idea was given in this answer so, be sure to give credits to the author.
You need to either:
Create a custom JPanel object
Create a class that extends JPanel
In any case you need to override the paintComponent(...) method.
Later, in that paintComponent() method you need to draw the image using Graphics#drawImage(...) method. This will make the JPanel to draw the image as the background.
After that you should override your JPanel's getPreferredSize() method, so you can call JFrame#pack(), which will resize your JFrame's size to its preferred size (which is the minimum size where all your components are visible).
After doing that, you can easily add components as you've always done:
panel.add(...);
And the second way is to make a JLabel to act as a Container, where you can add more Components to it (just like you do in a JPanel) (As shown in this answer)
The way to do this is:
Create a JLabel with an ImageIcon
Set its layout manager
Add components to it
Depending on which one you choose you have some differences:
Using the custom painting option, you need to take care of the preferred size of your container but you have more control over your component. However the image will fill all the space available on the window.
Using the JLabel option you can simply call pack() on your JFrame and it will resize to the image size, but if your image is too big your JFrame will be the same size too. If you resize your window to be shorter the image will be cropped and show "white" space if you make your window bigger.
This is how the image looks like with the 2 options, on the left the custom painting, on the right the label approach. At first they both look the same...
But... If we resize the window, this is what we get:
I like the custom painting approach more, but it depends on your needs and likes.
The code that produces the above output is:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JPanelWithBackgroundImageExample {
private JFrame frame; //Our window
private JPanel panel; //The panel where we're going to draw the background image
private Image image;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JPanelWithBackgroundImageExample().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
try {
image = ImageIO.read(new URL("https://i.stack.imgur.com/XZ4V5.jpg")); //We read the image from the Internet
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
panel = new JPanel() { //We need to open the curly braces so we can change the default behavior of the JPanel
/*
* This method is the one that paints the background, by default it paints it with gray color,
* so, we need to tell it to draw an image instead. (This method belongs to JPanel already, so we need to add
* "#Override" before it, so the compiler knows we're overriding it
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //Never forget this line or you could break the paint chain
/*
* This method belongs to the Graphics class and draws an image, this is what we want the JPanel to draw as our background
* The parameters are: the image to be drawn, the starting position (x, y) coords, the width and height and the observer
*/
g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), this);
}
/*
* This method is part of the JPanel, we're overriding it's preferred size and return the size we want
*/
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
};
JLabel label = new JLabel(new ImageIcon(image)); //We create a JLabel that will act as a container for our components
panel.setBorder(BorderFactory.createLineBorder(Color.WHITE)); //We create a border just for visibility of both ways
label.setBorder(BorderFactory.createLineBorder(Color.WHITE)); //We create a border just for visibility of both ways
label.setLayout(new BoxLayout(label, BoxLayout.PAGE_AXIS)); //We set the layout manager for the label
label.add(new JLabel("I'm a label inside a label")); //We add a new label to our label (that is acting as a container)
label.add(new JButton("I'm a button inside a label")); //We add a button to our label (that is acting as a container)
//You can add your components to the panel, as you always do it
panel.add(new JButton("HEY! I'm a button!")); //We add a button to our jpanel
panel.add(new JLabel("Click the button next to me! :D")); //We add a label to our jpanel
frame.add(panel, BorderLayout.WEST); //We add the pane which has a size of 300 x 200 to the left part of our JFrame
frame.add(label, BorderLayout.EAST); //We add the label (which acts as a container / jpanel) to the right part of our JFrame
frame.pack(); //We pack the frame, so it takes its preferred size (and as we only added a single component to it (the JPanel)
//As the panel has a size of 300 x 200, the frame will also have this size
frame.setVisible(true); //We set the visibility of the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Now, as a general tip, place your program on the Event Dispatch Thread (EDT) by changing your main() method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
And now, to answer your question in the comments:
everything works except the image because I don't have that image. so I copied an image url from google and pasted it in and it didn't appear? what can I do
Well, I think you took the code from the linked answer and changed this line:
frame.setContentPane(new JLabel(new ImageIcon("C:/Users/Frakcool/workspace/StackOverflowProjects/src/test/Air.jpg")));
To something like this:
frame.setContentPane(new JLabel(new ImageIcon("https://i.stack.imgur.com/XZ4V5.jpg")));
Well in that case, it's obvious that the code won't work that way, Swing doesn't know how to interpret a http in a String, but URL class does, and thus, you should change the above line like:
frame.setContentPane(new JLabel(new ImageIcon(new URL("https://i.stack.imgur.com/XZ4V5.jpg"))));
And import:
import java.net.URL;
In your class.
I hope this helps you in understanding how the code works, if not, well, I think you need to put more effort in understanding it.
Related
I am writing a small GUI in Java, using Swing components. My program uses several overlapping panels, the sizes of which are decided upon at the point that 'pack()' is called.
My problem is this. I need to know the dimensions of a particular JPanel prior to pack(), as I need to draw a line vertically down it. I cannot get this height dimension however until pack is called.
I have put in a System.out.println(myPanel.getSize()) call before the pack command & it returns (0, 0). When put in after, it returns the actual dimensions of the panel... as you would expect.
So, how does one draw a line on a panel down its entire length, either without knowing its length to begin with, or somehow doing so after pack has been called?
You can achieve this by adding a ComponentListener to the panel. Its componentResized() event is triggered whenever the panel is resized. And inside componentResized() method you will always get the actual size of the panel. Try below example and see it yourself.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class PanelResize
{
public static void main(String[] args)
{
CustomPanel panel = new CustomPanel();
panel.addComponentListener(new ComponentAdapter()
{
#Override
public void componentResized(ComponentEvent e)
{
System.out.println(panel.getSize());
panel.repaint();
}
});
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setBounds(300, 200, 400, 300);
f.setVisible(true);
}
}
class CustomPanel extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.blue);
g.drawLine(getWidth()/2, 0, getWidth()/2, getHeight());
}
}
Maybe i have encountered a bug or more probably doing something wrong ;)
I try to translate the content of a user drawn JPanel using a JScrollPanel. Inside the panel the drawing i would like to access the visible area through the Graphics class getClipBounds method to improve rendering performance.
Searching on SO brings a lot results referring to JScrollPane but none is mentioning a problem with the clip bounds. Google the same.
user drawn panel
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Content extends JPanel {
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
// intense clip bounds dependent rendering here
System.out.println(g.getClipBounds());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(2000,2000);
}
}
main frame setup
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import java.awt.BorderLayout;
public class ClipBoundsIssue {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClipBoundsIssue window = new ClipBoundsIssue();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ClipBoundsIssue() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
Content content = new Content();
scrollPane.setViewportView(content);
}
}
to reproduce
Just run the code, move one of the scrollbars and inspect the console output of System.out. The following picture depicted scrolling the bar on the x axis.
actual System.out result
Which produced the following results
java.awt.Rectangle[x=0,y=0,width=416,height=244]
java.awt.Rectangle[x=416,y=0,width=16,height=244]
java.awt.Rectangle[x=432,y=0,width=15,height=244]
java.awt.Rectangle[x=447,y=0,width=16,height=244]
java.awt.Rectangle[x=463,y=0,width=15,height=244]
expected result
I would have expected to have the width of the bounds to keep the same. But it changes from 416 to 16.
The question now is
Does anybody know why this happens, or how it can be avoided??
discared WAs
A possible workaround would be to lookup the view port's view bounds. But if possible i would like to avoid the Content class making any such lookup. Another alternative would be to pass the information into the Content class, but this i would like to avoid as well.
I would have expected to have the width of the bounds to keep the same.
Why? It is so simple that it is hard to explain, but let me try.
When you scrolling, only small new portion if the JPanel is appearing if you scroll slowly.
The produced output is absolutely correct:
java.awt.Rectangle[x=0,y=0,width=416,height=244] Control is shown first time, you need to redraw it completely
java.awt.Rectangle[x=416,y=0,width=16,height=244] You scrolled to the right by 16 pixels, so only narrow strip of you control must be redrawn.
You must understand that these coordinates are related to your control which has size set to 2000x2000 pixels.
Try to scroll the window created with this code and you will see what I am talking about:
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class ScrollPaneRepaintDemo extends JPanel {
public ScrollPaneRepaintDemo() {
setPreferredSize(new Dimension(2000,2000));
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new JScrollPane(new ScrollPaneRepaintDemo()));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override protected void paintComponent(Graphics g) {
Rectangle clip = g.getClipBounds();
g.setColor(new Color(new Random().nextInt()));
g.fillRect(clip.x, clip.y, clip.width, clip.height);
}
}
By the way - it works so because of JPanel's internal implementation. If you extend JComponent instead, the whole viewport will be clipped. I add also that JPanel repaints completely when resizing, its optimizations are only for scrolling.
My Question is the same as this:
How to get the size of JPanel which is not visible yet
But the answered posted there doesnt work for me. Im using the BorderLayout and Prefferedsize to manage 5 panels. My aim, see the title. revalidate() validate() pack() didnt work for me.
Other ideas?
Edit:
What im trying to do is a small paint program. Its build up in a simple border layout:
and the center pane is that to draw on. But i want to draw on a picutre, with getgraphics, and then draw this picure in the paintComponent.
so i have to set the size of the image to the size from the panel. And i want to prepare all this stuff, and after i call setVisible i want the panel to be ready to start drawing.
Edit 2:
Okay, my problem is half solved. I can set the size of the image after setVisible, i thought, but i had to wait a few seconds (usually 2) before the size was right. (i dont know why). Now it works with SwingUtilities.invokeLater, but also AFTER setVisible().
Isnt there any way to reach this?
Get size of jpanel before setVisible() called
JComponent returns its Size in two cases
is already visible on the screen
JFrame.pack() is called, notifier for LayoutManager, pack() could be called before setVisible()
(if Insets are used for coordinates) then is possible from NullLaoyut
If you have direct access to the main frame, you can call JFrame#pack which will validate the container hierarchy and layout all the child components, for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class BeforeVisible {
public static void main(String[] args) {
new BeforeVisible();
}
public BeforeVisible() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TestPane center = new TestPane(100, 100);
frame.add(center);
frame.add(new TestPane(100, 50), BorderLayout.NORTH);
frame.add(new TestPane(100, 50), BorderLayout.SOUTH);
frame.add(new TestPane(50, 100), BorderLayout.EAST);
frame.add(new TestPane(50, 100), BorderLayout.WEST);
System.out.println("Size beofre pack = " + frame.getSize() + "; " + center.getSize());
frame.pack();
System.out.println("Size after pack = " + frame.getSize() + "; " + center.getSize());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int pWidth, pHeight;
public TestPane(int width, int height) {
pWidth = width;
pHeight = height;
setBorder(new LineBorder(Color.RED));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(pWidth, pHeight);
}
}
}
Which outputs....
Size beofre pack = java.awt.Dimension[width=0,height=0]; java.awt.Dimension[width=0,height=0]
Size after pack = java.awt.Dimension[width=200,height=222]; java.awt.Dimension[width=100,height=100]
Now, if you don't have direct access to the main window, anything you do will be purely guess work.
You could also add a ComponentListener to the component(s) you are interested and monitor their changes in size...but this then raises the question of "why?"
The default size of some Component "A" (java.awt.Component, a abstract class who many swing elements exends, JPanel is a example) that not has value setted to its dimensions inside component "B" is setted after B shows, internally. So, you can not predict how is the size before component B shows because at this time the value properties of width and height of "A" is not setted yet.
You can manually get the height and width of component "A" before show it and set in B component. Or you can use a property file to store the sizes that you need and code a Util class to access it.
I am trying to re-position the button so that it is in the bottom right corner of the frame, but everything I try such as setLocation and setBounds, don't seem to do anything. Also, how would I change the button to an image? So that it is still a button, but an image is displayed.
package TrainCounselor;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Start extends JPanel {
public Start() {
// Game Properties
setOpaque(false);
setLayout(new FlowLayout());
}
public void paint(Graphics g) {
Image a = Toolkit
.getDefaultToolkit()
.getImage(
"C:/Users/Mel/workspace/camptycoon/javagame/src/javagame/background1.png");
g.drawImage(a, 0, 0, this);
super.paint(g);
}
public static void main(String[] args) {
JFrame myFrame = new JFrame("Put Image");
JButton startButton = new JButton("Start");
startButton.setLayout(null);
startButton.setLocation(50, 50);
Start c = new Start();
c.add(startButton);
myFrame.add(c);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setTitle("Counselor Training");
myFrame.setSize(755, 600);
myFrame.setResizable(false);
myFrame.setVisible(true);
}
}
I am trying to re-position the button so that it is in the bottom right corner of the frame
Use the appropriate Layout Managers.
I would start by using a JPanel with a FlowLayout that is right aligned. Then you add this panel to the "SOUTH" of the BoderLayout which is used by the JFrame.
See A Visual Guide to Layout Managers for more information.
Note when you add the Start class to the frame you are adding it to the "CENTER", not the south. Also, custom painting is done by override the paintComponent() method, not the paint() method and don't forget to invoke super.paintComponent() before you draw the image, not after.
Also, how would I change the button to an image? So that it is still a button, but an image is displayed.
Read the section from the Swing tutorial on How to Use Buttons.
I'm doing a project where i need some custom swing components. So far I have made a new button with a series of images (the Java Metal look doesn't fit with my UI at all). Ive implemented MouseListener on this new component and this is where my problem arises. My widget changes image on hover, click etc except my MouseListener picks up mouse entry into a the entire GridLayout container instead of into the image. So I have an image of about 200*100 and the surrounding container is about 400*200 and the mouseEntered method is fired when it enters that GridLayout section (even blank space parts of it) instead of over the image. How can I make it so that it is only fired when I hover over the image? Ive tried setting size and bounds and other attributes to no avail.
EDIT: Here's a demonstration of my issue. As you can see (sort of, colors are very similar) the bottom right button is highlighted just by entering its section of the GridlLayout. I only want it highlighted when I'm over the image actual, not the GridLayout section.
I Won't add the MouseListener methods because they just involve switching the displayed image.
public customWidget()
{
this.setLayout(new FlowLayout());
try {
imageDef=ImageIO.read(new File("/home/x101/Desktop/buttonDef.png"));
imageClick=ImageIO.read(new File("/home/x101/Desktop/buttonClick.png"));
imageHover=ImageIO.read(new File("/home/x101/Desktop/buttonHover.png"));
current=imageDef;
} catch (IOException e)
{
e.printStackTrace();
}
this.addMouseListener(this);
}
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
g.drawImage(current, 0, 0, current.getWidth(), current.getHeight(), null);
}
EDIT: added code section
As an alternative, consider the The Button API, which includes the method setRolloverIcon() "to make the button display the specified icon when the cursor passes over it."
Addendum: For example,
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ButtonIconTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
private static void createAndShowGui() {
String base = "http://download.oracle.com/"
+ "javase/tutorial/uiswing/examples/components/"
+ "RadioButtonDemoProject/src/components/images/";
ImageIcon dog = null;
ImageIcon pig = null;
try {
dog = new ImageIcon(new URL(base + "Dog.gif"));
pig = new ImageIcon(new URL(base + "Pig.gif"));
} catch (MalformedURLException ex) {
ex.printStackTrace(System.err);
return;
}
JFrame frame = new JFrame("Rollover Test");
JPanel panel = new JPanel();
panel.add(new JLabel(dog));
panel.add(new JLabel(pig));
JButton button = new JButton(dog);
button.setRolloverIcon(pig);
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
I assume your image contains ONLY 4 'customWidget' objects (in a 2x2 grid).
Your code is working as I would expect. Your MouseListener methods are responding to MouseEvents for 'customWidget' (not the image drawn in 'customWidget'), which is sized to take up 1/4 of the image, so they will respond when it enters the enlarged area. The error is actually in your Test program, because you are allowing the custom button widget to be larger than the image.
If you want a Test program that provides an image similar to yours, you should create a larger grid (say 4x4), and then only place your buttons in every other grid node. Place an empty component into the gaps.
Although I won't answer to your particular question, I hope this helps:
If the components just look wrong maybe you should reuse Swing components and just write a custom Look&Feel or theme.
It would certainly help ensuring the look of the application is consistent and at least you are using the right tool for the task you want to accomplish.
As a sidenote, be aware that Java comes with multiple Look&feels, including Look&Feels made to mimic the native OS theme.
See: http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html