0

I have a class Roundabout(JFrame), Surface(JPanel) and Spawn. In the Surface paintComponent I already draw some trafficlights, because they are always on the roundabout. But I also want to draw components at runtime. So I added a mousePressed event to the JFrame, the method I call here evetually calls spawnPedestrian from Spawn.java, I can see this because of the println. But when spawnPedestrian is called nothing is painted! If I create a pedestrian in the Surface class and paint it, it works. How I can make spawnPedestrian work?. I have tried to include the minimal code snippet (see below).

human human.png

track roundabout.png

public class Spawn {

public void spawnPedestrian(){
    //Create a new pedestrian.

    Pedestrian p = new Pedestrian(100,100);

    Roundabout.getSurface().add(p);
    Roundabout.getSurface().revalidate();
    Roundabout.getSurface().repaint();

   }
 }

public class Roundabout extends JFrame{

static Surface surface=new Surface(); 

public Roundabout(){
    initUI();
}

private void initUI() {

    setTitle("Roundabout");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    add(surface); 

    this.addMouseListener(new MouseAdapter() {// empty implementation of all
        // MouseListener`s methods
        @Override
        public void mousePressed(MouseEvent e) {
            System.out.println(e.getX() + "," + e.getY());
            Spawn spawn=new Spawn();
            spawn.spawnPedestrian();

        }
    });

    setSize(1618,850);
    setLocationRelativeTo(null);

}

public static JPanel getSurface() {  
    return surface;
}

public static void main(String[] args) {

    //Swing thread
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            Roundabout roundabout=new Roundabout(); 
            roundabout.setVisible(true);        
        }
    });                    
}

class Surface extends JPanel {

Track track=new Track();  

@Override
public void paintComponent(Graphics g) {

    super.paintComponent(g);    

    //Make sure the track is painted first
    track.paint(g);

    }

}

public class Track{

BufferedImage track;
Point trackPosition;
static final Point TRACK_POS = new Point(0, 0);
static final Point SENSOR_POS = new Point(250, 70);

public Track(){
    try {
        track = ImageIO.read(Roundabout.class.getResource("images/roundabout.png"));
    } catch (Exception ex) {
        System.out.println("Problem loading track image: " + ex);
    }
    trackPosition=new Point(TRACK_POS.x,TRACK_POS.y);
}

public void paint(Graphics g)
 {
    g.drawImage(track,TRACK_POS.x, TRACK_POS.y, null);
 }
}


public class Spawn {


//Needs x/y pos
public void spawnPedestrian(){
    Pedestrian p = new Pedestrian(30,50);

    System.out.println("Spawn me ");

    Roundabout.getSurface().add(p);
    Roundabout.getSurface().revalidate();
    Roundabout.getSurface().repaint();


    }
  }
}

public class Pedestrian extends JComponent {

BufferedImage pedestrian;
Point pedestrianPosition;
double pedestrianRotation = 0;
int pedestrianW, pedestrianH;

public Pedestrian(int x, int y){
    try {
        pedestrian = ImageIO.read(Car.class.getResource("images/human.png"));
    } catch (IOException e) {
        System.out.println("Problem loading pedestrian images: " + e);
    }

    pedestrianPosition = new Point(x,y);
    pedestrianW = pedestrian.getWidth();
    pedestrianH = pedestrian.getHeight();

}

public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.rotate(Math.toRadians(pedestrianRotation), pedestrianPosition.x, pedestrianPosition.y);
    g2d.drawImage(pedestrian, pedestrianPosition.x, pedestrianPosition.y, null);
}

EDIT : Thought I solved it by setting the Surface layout to null and and by using setBounds for Pedestrian. But the more I move the image to the right, the higher I have to set height and widthin setBounds(). Anyone who can help?

EDIT: Modified my post, it is a runnable example now, you only have to pick your own images for Track and Pedestrian.

EDIT: One copy paste action to get the code and images added.

Sybren
  • 1,071
  • 3
  • 17
  • 51
  • Which layout you are using for `Surface`? Can you replace `Pedestrian` instance with a standard `JButton` e.g. `Roundabout.getSurface().add(new JButton(""));` and check whether its getting painted or not? – Rupak May 19 '15 at 11:18
  • For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete Verifiable Example) or [SSCCE](http://www.sscce.org/) (Short, Self Contained, Correct Example). – Andrew Thompson May 19 '15 at 11:19
  • @AndrewThompson I don't know anything I can remove , this the most minimum I think, even it's still quite a lot code. – Sybren May 19 '15 at 11:22
  • @Rupak I don't use a layout for `Surface`. I tried your suggestion to add a `JButton` and it's is painted top center. But a `Pedestrian` doesn't get painted. Any ideas for a solution? – Sybren May 19 '15 at 11:24
  • *"I don't know anything I can remove"* It is not (necessarily) about 'removing' anything. I notice that `TrafficLight` is missing so that code would not compile. Therefore it ***cannot*** be considered an ***example*** of a run-time problem. Further, while an MCVE/SSCCE can be more than one class, it must be a single source file (with the rest of the classes demoted from `public` and pasted in after the source code with `main` method). Read the SSCCE link again. – Andrew Thompson May 19 '15 at 11:25
  • Spend some time on @AndrewThompson 's suggestion to improve your question, guessing what might be the problem without seeing the implementation is always hard. – Rupak May 19 '15 at 12:46
  • Improved the question , could you take a look? – Sybren May 19 '15 at 13:01
  • Now that is 6 copy/paste actions before the thing will compile. It needs to be ***one*** copy/paste as I mentioned in an earlier comment. – Andrew Thompson May 19 '15 at 13:09
  • *"you only have to pick your own images"* One way to get image(s) for an example is to **hot link** to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). – Andrew Thompson May 19 '15 at 13:11
  • @AndrewThompson editted the post, have a look please. – Sybren May 19 '15 at 13:20
  • @Rupak editted the post, have a look please. – Sybren May 19 '15 at 13:20
  • OK had a look. Here's a suggestion. Why don't **you** copy/paste the code above into a new project in your IDE and try to compile it? In fact, please do that with any code that is supposed to be an MCVE. If it is a run-time problem, I also expect that you would run it to check that the stated problem in fact does appear using the code. – Andrew Thompson May 19 '15 at 13:36
  • `ImageIO.read(Roundabout.class.getResource("images/roundabout.png"));` And by 'hot link' I mean more along the lines of `ImageIO.read(new URL("http://i.stack.imgur.com/2U3j5.png"));` .. Why don't people *ask* about things they do not understand? `` – Andrew Thompson May 19 '15 at 13:39
  • To make it run you only have to copy two images to a folder and create an images folder in your project and drag them in. – Sybren May 19 '15 at 13:47

1 Answers1

3

When you write Roundabout.getSurface().add(p);, you are adding a JComponent to a JPanel object. JPanels must have a layout to display subcomponents. By default, this is the FlowLayout (see doc). FlowLayout is meant to display components next to the others in a row. It won't support displaying components at arbitrary coordinates.

I am not sure why the pedestrians are not displayed, but the reason could be that they have a dimension of 0x0. You can check that easily by using:

p.setPreferredSize(new Dimension(50,50));

Depending on your needs, you could follow a different approach: Instead of extending JPanel, Surface could be a simple Canvas, holding a list of "Paintables", such as the Pedestrians. In Surface.paintComponent(), you can call each Paintable.paint(), providing them the Graphics object:

interface Paintable {
   public void paint(Graphics2D g);
}

class Pedestrian implements Paintable {
   public double x, y;
   public void paint(Graphics2D g){
      Shape circle = new Ellipse2D.Double(x - 5, y - 5, 10, 10);
      g2d.draw(circle);
   }
}

class Surface extends Canvas {

   public List<Paintable> components = new ArrayList<Paintable>();

   @Override
   public void paintComponent(Graphics g) {

      super.paintComponent(g);    

      for(Paintable p : components)
         p.paint((Graphics2D) g);

   }

}

EDIT: I spotted a few additional problems with your code:

  • Surface.java: Note that, as is, you use the default FlowLayout (so it won't manage displaying components at given coordinates).
  • Pedestrian.java: Add setPreferredSize(Roundabout.getSurface().getSize()); at the end of the constructor. Replace your paint() method with:

.

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.rotate(Math.toRadians(pedestrianRotation), 
        pedestrianPosition.x, pedestrianPosition.y);
    g2d.drawImage(pedestrian, pedestrianPosition.x,
        pedestrianPosition.y, null);
}
  • RoundAbout.java: surface =new Surface(); should be called inside initUI() so that it is instanciated on the Event Thread.
mKorbel
  • 109,525
  • 20
  • 134
  • 319
Eric Leibenguth
  • 4,167
  • 3
  • 24
  • 51
  • I tried `setPrefferedSize` but the `Pedestrian` still isn't visilble. And why do I have to change `Surface` to `Canvas` I tried that solution but nothing was painted – Sybren May 19 '15 at 11:46
  • Found a solution see my post – Sybren May 19 '15 at 11:55
  • The idea behind `Canvas` is that you might not need a container such as JPanel. `Canvas` is enough for drawing shapes and images. Can you try to hard-code a pedestrian inside Surface.java? That way you can see if it is a problem with the Pedestrian's display or with the fact that they are displayed dynamically. – Eric Leibenguth May 19 '15 at 12:57
  • Tried your suggestions, but stills no `Pedestrian` is painted – Sybren May 19 '15 at 14:17
  • One more try: the problem comes from the size of the pedestrian, which is too small: I updated my post with: `setPreferredSize(Roundabout.getSurface().getSize());` That way you are sure that whever the image is painted, it will not be cropped by the size of the Pedestrian JComponent. – Eric Leibenguth May 19 '15 at 14:40