Matheus Simões
Matheus Simões

Reputation: 11

Implementing a Image loader in Java Swing/Awt

I'm implementing a Paint aplication that loads from a file and can edit it. But my Openfile function doesn't function correctely, it loads the image and changes the Bounds correctely but dont paint it in the Jpanel.

Here's my code.

public class OpenSave extends JPanel{

private static final long serialVersionUID = 1L;

protected BufferedImage imagem;

public OpenSave() {}

public void open(JPanel panel) throws IOException {
    JFileChooser escolher = new JFileChooser();
    escolher.setCurrentDirectory(new File("."));

    escolher.setFileFilter(new javax.swing.filechooser.FileFilter() {
        public boolean accept(File f) {
            return f.getName().toLowerCase().endsWith(".jpg") || f.isDirectory();
    }
    public String getDescription() {
         return "Imagens JPG";
    }
    });

    escolher.setFileFilter(new javax.swing.filechooser.FileFilter() {
        public boolean accept(File f) {
            return f.getName().toLowerCase().endsWith(".png") || f.isDirectory();
        }
    public String getDescription(){
        return "Imagens PNG";
    }
    });


    JFrame frame = new JFrame();
    frame.setIconImage(Toolkit.getDefaultToolkit().getImage(OpenSave.class.getResource("/javax/swing/plaf/metal/icons/ocean/directory.gif")));
    int r = escolher.showOpenDialog(frame);
    if (r == JFileChooser.APPROVE_OPTION) {
        imagem = ImageIO.read(new File(escolher.getSelectedFile().getAbsolutePath()));
        panel.setBounds(77, 13, imagem.getWidth(this), imagem.getHeight(this));
    }
  }


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

With this i can save and load correctly but the loaded image don't go to my JPanel panel.

here is the main class simplified.

    private JPanel contentPane;
    private Jpanel panel;

    public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                TelaMain frame = new TelaMain();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    }

    public TelaMain() {

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 600, 400);

    JMenuBar menuBar = new JMenuBar();
    menuBar.setFocusTraversalKeysEnabled(true);
    setJMenuBar(menuBar);

    JMenu mnArchive = new JMenu("Archive");
    menuBar.add(mnArchive);
    JMenuItem mntmOpen = new JMenuItem("Open");
    mnArquivo.add(mntmOpen);

    panel = new JPanel();
    panel.setAutoscrolls(true);
    panel.setBorder(new LineBorder(new Color(0, 0, 0)));
    panel.setBackground(Color.WHITE);
    panel.setBounds(77, 13, 498, 307);
    contentPane.add(panel);

    mntmAbrir.addActionListener(
        new ActionListener() {
            public void actionPerformed(ActionEvent abrir) {
                    OpenSave a = new OpenSave();
                    try {
                        a.open(panel);
                        a.repaint();

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    );

i'm open to any other edit sugestions to my question.

Upvotes: 0

Views: 508

Answers (2)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Your code looks confused. Specifically:

  • You create a JFrame but never display it, perhaps simply to use as a parameter for your file chooser?
  • You pass a JPanel into your open method, only to change its size but you do nothing else to it.
  • I have the feeling that you think that this passed in JPanel will somehow obtain the image that you're reading in, but if so, you're mistaken.
  • The image may be displayed within the OpenSave JPanel, but we never know since we never see you add it to any top-level Window such as a JFrame -- so your current code gives us an incomplete picture of what you're doing.
  • Your paintComponent method is dangerous in that it tries to draw the image in the OpenSave JPanel without checking first if the image is null. This risks the code throwing a NullPointerException if no image is currently available.
  • You're using setBounds(...) a dangerous thing to do. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.

Suggestions:

  • Be sure that you're placing your OpenSave JPanel into a displayed JFrame
  • Within its paintComponent method, only paint the image if it is not null. In other words check null in an if block and only paint if (imagem != null) {}
  • Show us the rest of your pertinent code
  • Pass the currently displayed JFrame into the JFileChooser, not a non-displayed one.
  • Have your OpenSave JPanel override the getPreferredSize, and make that size the size of the image it holds.
  • Myself, I'd separate my image file opening and saving code from the view code, the GUI code.

Portions of an example:

public void setImagem(BufferedImage imagem) {
    this.imagem = imagem;
    repaint();
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (imagem != null) {
        g.drawImage(imagem, 0, 0, this);
    }
}

@Override
public Dimension getPreferredSize() {
    Dimension sz = super.getPreferredSize();
    if (imagem != null) {
        sz = new Dimension(imagem.getWidth(), imagem.getHeight());
    }
    return sz;
}

private static void createAndShowGui() {
    MyOpenSave openSave = new MyOpenSave();

    JFrame frame = new JFrame("MyOpenSave");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(openSave);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> createAndShowGui());
}

A more complete example:

TestOpenSave.java -- the "driver" class

public class TestOpenSave {

    private static void createAndShowGui() {
        OpenSave openSave = new OpenSave();
        JFrame frame = new JFrame("OpenSave");

        JPanel btnPanel = new JPanel();
        btnPanel.add(new JButton(new GetImageAction("Open Image", frame, openSave)));

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(openSave);
        frame.add(btnPanel, BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

OpenSave.java, the "view" class that displays the image

@SuppressWarnings("serial")
public class OpenSave extends JPanel {
    private BufferedImage imagem;

    public void setImagem(BufferedImage imagem) {
        this.imagem = imagem;
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (imagem != null) {
            g.drawImage(imagem, 0, 0, this);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension sz = super.getPreferredSize();
        if (imagem != null) {
            sz = new Dimension(imagem.getWidth(), imagem.getHeight());
        }
        return sz;
    }
}

The GetImageAction file loading class or "controller" class. Note that it is separate from the image display or "view class"

@SuppressWarnings("serial")
public class GetImageAction extends AbstractAction {
    private JFrame myFrame;
    private OpenSave openSave;

    public GetImageAction(String name, JFrame myFrame, OpenSave openSave) {
        super(name);
        this.myFrame = myFrame;
        this.openSave = openSave;
        int mnemonic = (int) name.charAt(0);
        putValue(MNEMONIC_KEY, mnemonic);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser escolher = new JFileChooser();
        escolher.setMultiSelectionEnabled(false);
        escolher.setCurrentDirectory(new File("."));
        escolher.setFileFilter(new FileFilter() {

            @Override
            public String getDescription() {
                return "Imagens JPG and PNG";
            }

            @Override
            public boolean accept(File f) {
                String ext = f.getName().toLowerCase();
                if (f.isDirectory() || ext.endsWith(".jpg") || ext.endsWith(".png")) {
                    return true;
                }
                return false;
            }
        });
        int reply = escolher.showOpenDialog(myFrame);
        if (reply == JFileChooser.APPROVE_OPTION) {
            try {
                BufferedImage img = ImageIO.read(escolher.getSelectedFile());
                openSave.setImagem(img);
                myFrame.pack();
                myFrame.setLocationRelativeTo(null);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

Upvotes: 4

Justin
Justin

Reputation: 31

I'm not entirely sure, but the issue may be that you didn't include frame.add(this) when you were initializing the JFrame, which tells the program to use the paint method in your class.

Upvotes: 0

Related Questions