MASSIVE EDIT: I added more descritpion and code.
I recently encounter a problem where a panel that I added to another panel won't display properly (only display a black dot instead of the image). The flow of the code is: the Menu class has a button panel. When the button start is press, the Menu remove the button panel, create a Board object(that implement panel) and add it to Menu. In Board constructor, an image is loaded (a .png), then a PlayerPanel (that implement panel) is added to the board panel. In PlayerPanl constructor, an image is loaded.
The plan is to make the menu repaint() method able to call Board paintcomponent. Board will then ask PlayerPanel to paintComponent. PlayerPanel paint his image, Board paint his image and that's it, both image should display.
public class Menu extends JFrame implements ActionListener
{
Board theBoard;
JPanel pnlButton = new JPanel();
JButton btnStart = new Jbutton("Start");
public Menu(String s)
{
pnlButton.add(btnStart);
super.add(pnlButton);
super.setLocation(0,0);
super.setSize(600, 500);
super.setResizable(false);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent evt)
{
if(evt.getSource() == btnStart)
{
theBoard = new Board ("TestBoard");
super.remove(pnlButton);
super.add(theBoard);
super.repaint();
}
}
}
public class Board extends JPanel
{
BufferedImage boardImage;
PlayerPanel playerPanel;
public Board (String boardName)
{
boardImage = Tools.loadImage(boardName);
playerPanel = new PlayerPanel();
this.add(playerPanel);
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(boardImage, 0, 200, null);
}
}
public class PlayerPanel extends JPanel
{
BufferedImage playerImage;
public PlayerPanel()
{
playerImage = Tools.loadImage("TestPlayer");
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(playerImage, 0, 0, null);
}
}
Both image load successfully as tested, but when the repaint() is call from the JFrame holding the Board panel, only the image in Board is painted, and the image in PlayerPanel is replace with a black dot.
Any help? Thanks!
Your problem seems to be due to the size of your PlayerPanel JPanel. Sometimes it helps to debug things by testing to see what size things are when they're rendered. For instance, if you change your Board paintComponent method to this:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (boardImage != null) {
g.drawImage(boardImage, 0, 200, null);
}
System.out.printf("Board size: [%d, %d]%n", getWidth(), getHeight());
System.out.printf("Player size: [%d, %d]%n", playerPanel.getWidth(), playerPanel.getHeight());
}
it will tell you exactly what the sizes are of itself and its constituent playerPanel.
To solve the issue, you could give Board a layout manager that expands its constituent components such as a BorderLayout. Another possible solution is to give PlayerPanel its own getPreferredSize() method override, especially if you want it to size itself to its image. e.g.,
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (playerImage != null) {
g.drawImage(playerImage, 0, 0, null);
}
}
Alternatively, you could use a JLabel that holds an ImageIcon rather than draw in the JPanel.
Note, you should swap views with a CardLayout rather than calling remove(...), add(...), revalidate(), repaint()
Related
I have read a lot of answers about this problem but I can't manage to find my error even on a simple code. Here is the problem : I'd like to draw an Image in a JLabel which is in a JPanel, but the paintComponent() method of the JLabel isn't called.
Here is the code :
The ImagePainter class should draw an image
public class ImagePainter extends JLabel{
private Image image;
public ImagePainter(){
try {
image = ImageIO.read(new File("src/testgui/image.png"));
} catch (IOException exception) {
exception.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
System.out.println("in paintComponent");
}
}
Here is a standard JFrame. I took care to add the JPanel to the contentPane
public class Display extends JFrame{
public Display(){
JPanel jp = new JPanel();
ImagePainter i = new ImagePainter();
getContentPane().add(jp);
jp.add(i);
jp.repaint();
setSize(800, 800);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
And finally the main. I instanciate Display on the EDT like everyone tell to do :
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
Display d = new Display();
}
});
}
}
Finally, I observed that if I directly add the ImagePainter to the contentPane, the image is drawn correctly. It's probably a stupid error but I spend literally hours trying to find it and really can't see it. Thank you in advance !
The label does not account for the preferred size of the image when the image is custom painted! The panel by default has a flow layout. A flow layout does not stretch components to fit. So that label would have size of 0 x 0 pixels. You can confirm that by adding a visible border to the label.
But given the image is displayed in a label, why not just set the image as the icon of the label?
Also, the jp.repaint() statement in the Display constructor is useless, since you have not yet set the frame visible.
I have two panels defined like:
public class JPanel_with_BG extends JPanel
{
private Image bg_image;
public JPanel_with_BG(Image bg_image)
{
this.bg_image = bg_image;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (bg_image != null)
{
g.drawImage(bg_image, 0, 0, this);
}
}
}
and,
public class MPanel extends JPanel
{
public void paint(Graphics g)
{
super.paint(g);
// draw something...
}
}
Then, I want to add the second panel over the bg_pnl. The top panel is smaller like bg_pnl.size = pnl + 2*padding.
MPanel pnl = new MPanel();
JPanel bg_pnl = new JPanel_with_BG(image);
int pad = 50;
bg_pnl.setBorder(new EmptyBorder(pad, pad, pad, pad));
bg_pnl.add(pnl);
The problem is that what I'm drawing on the top panel is not visible. What I can see is only the background image. Any ideas? Thanks.
You Code seems ok. I tried this at my end and I can see a small area on my UI showing MPanel. You need to validate the size of your panel on which you are showing this component.
It might happen that the area is not visible because of the dimension of the window. Also a panel's default layout is flow layout and it arranges the components added on it based on their size and if they are really small, It might not be visible
I've been working on a drinking game program for school.
//this is the game //http://sotallytober.com/games/verbal/mexican/
Anyway, I painted a image in an JPanel using the following code (it's an class that extends JPanel)
public class iconPanel extends JPanel {
ImageIcon image;
Image pic;
public iconPanel(String startScreenImage) {
image = new ImageIcon(startScreenImage);
pic = image.getImage();
this.setBackground(new Color(0, true));
}
#Override
public void paintComponent(Graphics g) {
//Paint background first
g.drawImage (pic, 0, 0, getWidth (), getHeight (), this);
}
Now in my other class, where I have the layout and all the components I declare on top my JPanels like this :
private JPanel pnDrinkPlayerBW;
Then in a method in the same class named MakeComponents I set the JPanel to :
pnDrinkPlayerBW = new iconPanel("img/glass.jpg");
pnDrinkPlayerBW.setPreferredSize(new Dimension(183,61));
Afterwards I add it to the Panel where it has to come, and that panel onto the frame in the method makeLayout() (I don't think that it's useful code, so if you want to see it, ask me)
Then if a button gets pressed, I want to change the glass.jpg image to another image, for example beerGlass0.png, so in the actionlistener in another method actionEvents() I do this:
pnDrinkPlayerBW = new iconPanel("img/beerGlass.png");
pnDrinkPlayerBW.setPreferredSize(new Dimension(183,61));
pnDrinkPlayerBW.repaint();
I'll put the constructor of this class also here, just if people need it :
public SpelScreen(){
makeComponents();
makeLayout();
actionEvents();
} // note : this is'nt the full constructor, just the call for the methods I talked about, SpelScreen extends JFrame..
So what I want to do, is to set in the class SpelScreen a new image for the iconPanel and repaint it using the same instance of the spelscreen.
I am quite new to Java, so don't expect me to rapidly understand complicated code :)
Thanks!
First off you're forgetting to call super.paintComponent in your paintComponent method. Also paintComponent should be protected not public
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(..);
}
Second, I don't think you want to create a new iconPanel and immediately call repaint on it. That will probably do nothing.
Instead have a setter for your pic, then just repaint(); inside that method.
public void setPic(Image pic) {
this.pic = pic;
repaint();
}
Then you can just call the setPic from the the class you created the iconPanel in. For example
iconPanel panel = new iconPanel("...");
... // then in some listener
public void actionPerformed(ActionEvent e) {
Image pic = null;
try {
pic = ImageIO.read(...);
panel.setPic(pic);
} catch ...
}
Another option is just to have an array of images you initialize in the iconPanel. Then in a a listener, you can just change the index the if the image array then call repaint. Something like this
Image[] images = new Image[5];
int imageIndex = 0;
// fill the array with images
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(images[imageIndex], ...);
}
Then you could just change the imageIndex and repaint()
Side Note
You should be using Java naming convention. Class names being with capital letters i.e. iconPanel → IconPanel
Update
Using ImageIcon
public void setImage(ImageIcon img) {
pic = img.getImage();
repaint();
}
I need to pass a JPanel extending class to the main class.
Here is what I have so far:
Main class
import java.awt.Color;
import javax.swing.*;
public class main {
private gamePanel gamepanel = new gamePanel();
public JPanel createContentPane(){
// We create a bottom JPanel to place everything on.
JPanel totalGUI = new JPanel();
//We set the Layout Manager to null so we can manually place
// the Panels.
totalGUI.setLayout(null);
//Now we create a new panel and add it to the bottom JPanel.
totalGUI.add(gamepanel);
totalGUI.setOpaque(true);
return totalGUI;
}
private static void createAndShowGUI(){
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("[=] There's a JPanel in here! [=]");
//Create and set up the content pane.
main demo = new main();
frame.setContentPane(demo.createContentPane());
//The other bits and pieces that make your program a bit more stable.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(700,500);
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a jog for the event-dispatching thread:
//creating and showing this application's GUI.
System.out.println(gamepanel);
SwingUtilities.invokeLater(new Runnable() {
public void run(){
createAndShowGUI();
}
});
}
}
The gamePanel class
public class gamePanel extends JPanel implements Commons {
private Dimension d;
private ArrayList snowmens;
private coreFunctions functions = new coreFunctions();
private int snowmenX = 150;
private int snowmenY = 5;
private final String snowmenpix = "snowman.png";
private JPanel background;
public gamePanel() {
add(new JLabel("TEST"));
setFocusable(true);
setDoubleBuffered(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, 700, 700);
}
}
I can't figure out why the blue background and the label is not being shown...
EDIT:
Here are more details:
Ok so I am trying to make a little 2D game. For that I need to create some snowmen on the gamePanel class and display it via the main class. To start it off, the createContentPane creates a background panel, the root panel if you want. The createandshowgui creates a JFrame.
The gamepanel class is in fact a JPanel which has 1 panel as of now, which is the background panel. For now, I only want it to have a blue background.
I tried putting it like this because I saw some examples and they were pretty similar to what I have, but for some reason, I can't get it to work....
Thank you,
Ara
You should use LayoutManager instead of setLayout(null); and if only one component is being added no need for it either (that component will stretch to fill unless others are added)
see here for tutorial on LayoutManagers:
http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
If all you are trying to do is have a blue background (on which you can add components) then simply override paintComponent() of your gamePanel and paint the background blue:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
then you can add your JLabel as if it was a normal Panel as the background is now being painted blue and not set by/as a component.
If you have an image look into g.drawImage(Image image,int x,int y,ImageObserver iO)
EDIT
DROP THE CALL TO:
setLayout(null);
and it will work
I have a JFrame with BorderLayout, there are panels on all sides (North, East ,...). In the panels there are labels and buttons mostly.
Now I want the frame to have a background image, some research told me that i had to change the content pane of my frame.
When I try this however, the content gets put in the background and isn't visible. Also, I don't know how to resize the image if the frame is resized.
Is there an easy fix for this or will I have to rework most of my code?
put JPanel (or JComponent) with background Image to the BorderLayout.CENTER, then this JPanel fills whole JFrame area, rest of yout JComponents put to this JPanel
there are Jpanels on all sides (North, East ,...). In the Jpanels there are Jlabels and Jbuttons mostly.
these JComponents covered all available Rectangle for JFrame, then Background Image (from my 1st point) never will be dispalyed, because these JComponents are on_top JFrame and could be hide this Image as well,
add JPanel with Background Image (from my 1st point), then put there another JPanel(s) with JPanel#setOpaque(false);, then this JPanel will be transparent, notice JPanel has implemented by default FlowLayout
frame.getContentPane().add(new JPanel() {
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight());
}
});
This example will get you started. Use it like any JPanel.
public class JPanelWithBackground extends JPanel {
Image imageOrg = null;
Image image = null;
{
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
int w = JPanelWithBackground.this.getWidth();
int h = JPanelWithBackground.this.getHeight();
image = w>0&&h>0?imageOrg.getScaledInstance(w,h,
java.awt.Image.SCALE_SMOOTH):imageOrg;
JPanelWithBackground.this.repaint();
}
});
}
public JPanelWithBackground(Image i) {
imageOrg=i;
image=i;
setOpaque(false);
}
public void paint(Graphics g) {
if (image!=null) g.drawImage(image, 0, 0, null);
super.paint(g);
}
}
Usage Example:
Image image = your image
JFrame f = new JFrame("");
JPanel j = new JPanelWithBackground(image);
j.setLayout(new FlowLayout());
j.add(new JButton("YoYo"));
j.add(new JButton("MaMa"));
f.add(j);
f.setVisible(true);