Ben Wainwright
Ben Wainwright

Reputation: 4611

Why does drawImage fail when I am using it inside a Jpanel

I am doing the initial section of a simple platform game in Java. I have created a class called entity which extends JPanel and successfully added it to the window.

import javax.swing.*;
import java.awt.*;

/**
 * Created by bw12954 on 27/05/16.
 */
public abstract class Entity extends JPanel {

   private final SpriteSheet sprites;

   private       Point       location;
   private       Dimension   dimensions;

   public Entity(int x, int y, int w, int h, SpriteSheet sprites)
   {
      location     = new Point(x, y);
      dimensions   = new Dimension(w, h);
      this.sprites = sprites;
   }

   public Entity(int x, int y, int w, int h)
   {
      this(x, y, w, h, null);
   }

   @Override
   public Dimension getPreferredSize()
   {
      return dimensions;
   }

   public void setLocation(int x, int y)
   {
      location.setLocation(x, y);
   }

   /* Some code removed here for brevity */

   @Override
   public void paintComponent(Graphics g)
   {
      super.paintComponent(g);
      g.drawImage(sprites.get(),
                  (int)location.getX(),
                  (int)location.getY(),
                  null);
   }

}

If I add this directly to the JFrame as below, then the graphic shows up on the window as I would expect it to (note that Player is a very simple subclass of Entity)

public class Window {

   private JFrame window;

   public Window()
   {
      SwingUtilities.invokeLater(this::run);
   }

   private void run()
   {
      try {
         window = new JFrame();
         window.setDefaultCloseOperation(window.EXIT_ON_CLOSE);
         window.setLocationByPlatform(true);
         window.setUndecorated(true);
         Player p = new Player(0,0);
         window.add(p);
         window.setExtendedState(JFrame.MAXIMIZED_BOTH);
         window.setVisible(true);
      } catch (IOException e) {
         // TODO handle exception
         e.printStackTrace();
      }
   }
}

However - when I create a class called World which also extends JPanel, add that to the window instead, then use the add() method in its constructor to add a new Player to it, it doesn't appear. Interestingly, if I add setBackground() to the constructor for Player/Entity, I can see a coloured square where the entity SHOULD be. Its just that drawImage doesn't appear to work.

If anyone else has any idea what is going on here, it will be greatfully appreciated!

Upvotes: 0

Views: 33

Answers (1)

camickr
camickr

Reputation: 324118

If I add this directly to the JFrame as below, then the graphic shows up on the window as I would expect it to

The default layout manager of the content pane of the frame is a BorderLayout. Any component added to the frame without a constraint will be added to the "CENTER" which means the component is automatically resized to fill the entire space.

However - when I create a class called World which also extends JPanel, add that to the window instead, then use the add() method in its constructor to add a new Player to it, it doesn't appear.

The default layout manager of a JPanel is a FlowLayout, which respects the preferred size of any component added to it and will reset the location of the component based on the rules of the layout manager.

Interestingly, if I add setBackground() to the constructor for Player/Entity, I can see a coloured square where the entity SHOULD be. Its just that drawImage doesn't appear to work

Probably because your preferred size calculation is incorrect and the image is truncated.

  location     = new Point(x, y);
  dimensions   = new Dimension(w, h);

The above code will only work if the Point is (0, 0). The more general case code should be:

  location     = new Point(x, y);
  dimensions   = new Dimension(x + w, y + h);

because you need to consider where you actually paint the image realative to the component.

So when you do this you should see the image, however you will not see the image in the proper location because the layout manager will override the location.

So it you want to continue with this approach of using components you will need to use a null layout, which means you will manually need to use the setSize(...) and setLocation(...) of each component. In this case the size of the component will be the (w, h) and you will draw the image using:

  g.drawImage(sprites.get(), 0, 0, this);

Note however if you use the component approach there is no need to even create a custom component. You could just use a JLabel with an ImageIcon. You would assign the Icon when you create the label.

Upvotes: 1

Related Questions