-1

I am creating a simple application for test purposes that displays a couple of JFrames separately, and each one contains an image.

The problem is that it displays the number JFrames expected based on the count of images on the list, however, only the last one presents the image. All the other ones are empty.

I've already made a debug, and check that the Buffered Image list is exactly as expected, all objects are there, and they are copies of each other as it is a test.

Is there anything wrong with the repaint call (already tried in a variety of places) or with the ImgPanel subclass? That's my only guess of what could be wrong.

public class Main() extends JFrame{

   BufferedImage I;

   public void show(ArrayList<BufferedImage> images){
       int i = 0;
       for(BufferedImage b : images){
           this.I = b;

           Container c = getContentPane();
           c.setLayout(null);
           c.add(new ImgPanel());

           JFrame frame = new JFrame();
           frame.validate();
           frame.repaint();

           frame.setTitle("Execution: "+i);
           frame.getContentPane().add(c);
           frame.setSize(800,600);
           frame.setVisible(true);
           i++;
       }
   }

   public class ImgPanel extends JPanel {
       public imgPanel() { 
           setBounds(0, 0, 800,600);  
       }

       @Override
       public void paint(Graphics g) {
           super.paint(g);
           g.drawImage(I, 0, 0, this);
       }
   }

   public static void main(String[] args){
       //"d" is an Object Already declared
       ArrayList<BufferedImage> images = d.getImages();
       Main m = new Main();
       m.show(images);
   }
}
Patrick Bard
  • 1,804
  • 18
  • 40
  • See [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/a/9554657/418556) – Andrew Thompson May 22 '14 at 23:22
  • @AndrewThompson As sad, this is just for test. I need a visible test. I will not use this on a real application. – Patrick Bard May 22 '14 at 23:49
  • `//"d" is an Object Already declared` One way to get image(s) for an example is to hot-link to the images seen in [this answer](http://stackoverflow.com/a/19209651/418556). For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). – Andrew Thompson May 23 '14 at 00:02
  • 1
    `c.setLayout(null);` 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. To organize the components for a robust GUI, instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556), along with layout padding & borders for [white space](http://stackoverflow.com/q/17874717/418556). BTW - why does the code not simply display each image in a `JLabel`? – Andrew Thompson May 23 '14 at 00:03
  • What's `getContentPane`? Your code can't compile without – MadProgrammer May 23 '14 at 00:14
  • @MadProgrammer Sorry, Some parts I didn't just copy pasted here, I forgot that Main extends JFrame. – Patrick Bard May 23 '14 at 01:06
  • @PatrickBard Then my answer stands, you can't add the content pane from one panel to another without it been removed from the first... – MadProgrammer May 23 '14 at 01:08
  • @MadProgrammer Yes, I was just reading the answers and understanding what you said. I do not a have a big java Experience, didn't know that. – Patrick Bard May 23 '14 at 01:22
  • @PatrickBard You'd be surprised by how many people get bitten by that gotcha – MadProgrammer May 23 '14 at 01:24
  • @MadProgrammer Your comparison that made me understand. Actually, I knew that simple example you gave about changing colours, but it did't came to my mind that was a similar case before. – Patrick Bard May 23 '14 at 01:45

3 Answers3

4

I'm not sure where the problem was since your code did not compile. This code works (rock solid) reliably. In this GUI, I would tend to replace ImgPanel with a JLabel, YMMV.

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.net.URL;

public class Main {

   public void show(ArrayList<BufferedImage> images){
       int i = 0;
       for(BufferedImage b : images){

           JFrame frame = new JFrame("Execution: "+i);
           frame.add(new ImgPanel(b));
           frame.pack();
           frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
           frame.setLocationByPlatform(true);
           frame.setVisible(true);
           i++;
       }
   }

   public class ImgPanel extends JPanel {

       BufferedImage i;

       public ImgPanel(BufferedImage image) {
           i = image;
       }

       @Override
       public Dimension getPreferredSize() {
           return new Dimension(i.getWidth(), i.getHeight());
       }

       @Override
       public void paintComponent(Graphics g) {
           super.paintComponent(g);
           g.drawImage(i, 0, 0, this);
       }
   }

   public static void main(String[] args) throws Exception {
       final ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
       String[] urlStrings = {
           "https://i.stack.imgur.com/XZ4V5.jpg",
           "https://i.stack.imgur.com/7bI1Y.jpg",
           "https://i.stack.imgur.com/OVOg3.jpg",
           "https://i.stack.imgur.com/lxthA.jpg"
       };
       for (String s : urlStrings) {
           URL url = new URL(s);
           BufferedImage bi = ImageIO.read(url);
           images.add(bi);
       }
       Runnable r = new Runnable() {
           public void run() {
               Main m = new Main();
               m.show(images);
           }
       };
       SwingUtilities.invokeLater(r);
   }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 2
    Gee, and there I was about to add an example of my own :P +1 for a runnable example – MadProgrammer May 23 '14 at 00:21
  • @MadProgrammer Personally I think your 'point by point' is more constructive. By the time I've fixed code, it is so different I forgot what I did. ;) – Andrew Thompson May 23 '14 at 00:22
  • I said what was my output, so I thought that was easier to think that if it did not compile, it was probably a typing mistake and not that I was executing a code that does not compile. – Patrick Bard May 23 '14 at 01:27
4

Based on your, incomplete, code example and the description of your problem, I would suggest that the problem has to do with your attempt to add a single instance of a component to multiple containers, for example...

Container c = getContentPane();
c.setLayout(null);
c.add(new ImgPanel());
//...
frame.getContentPane().add(c);

A component can only reside in a single container, when a component is added to a new container, it is first removed from it's current container (if it has a parent).

You can test this by setting the ImgPanel background color to something else, like setBackgroundColor(Color.RED), the only frame that will appear red should be the last one...

  • You should avoid null layouts, in this specific case, you could does not take into account the borders frames, meaning the content area will expand beyond the viewable area of the frame. Frame borders are also different sizes on different look and feels/platforms
  • It's generally encouraged to use paintComponent over paint as it reduces the risks of weirdness and potentially breaking the paint chain
  • I'll overlook the fact that it would be simpler to use a JLabel to accomplish this
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thank you for your example. I normally prefer something that show me what's wrong and why. You made me understand the mistake, what I really wanted. – Patrick Bard May 23 '14 at 01:42
  • @PatrickBard Understanding is more powerful than copying ;) ... although sometimes, you just need a copy :P – MadProgrammer May 23 '14 at 01:44
  • Exactly, I first accepted @Andrew's answer, which came first, is very good, and help me accomplishing what I wanted. But then I remember, that I really wanted to know what was the problem, and you did great showing it. – Patrick Bard May 23 '14 at 01:48
1

When using a JPanel, use paintComponent(Graphics)

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
}
Christian Tucker
  • 627
  • 8
  • 20
  • Ok, I already use that on some projects. But this one was based on one that use that internal class with the `paint()` method, and it working normal with one JFrame, the repaint work perfectly with one JFrame too. – Patrick Bard May 22 '14 at 23:51
  • Replaced the `paint()` method with the `paintComponent()` and I got the same output. – Patrick Bard May 22 '14 at 23:53