konewka
konewka

Reputation: 650

How can I refresh a JFrame with BufferedImage in it?

In addition to my question How can I more quickly render my array?, I made the following class to make a JFrame:

package myprojects;

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

class BackgroundImageJFrame extends JFrame {
    public BackgroundImageJFrame(BufferedImage img) {
        setTitle("Background Color for JFrame");
        int h = img.getHeight();
        int w = img.getWidth();
        setSize(w, h);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        setLayout(new BorderLayout());
        setContentPane(new JLabel(new ImageIcon(img)));
        setLayout(new FlowLayout());
        // Just for refresh :) Not optional!
        setSize(w-1, h-1);
        setSize(w, h);
    }
}

which I call with new BackgroundImageJFrame(img);. As I want to refresh the contents of this JFrame, this doesn't work optimally, since this creates a new JFrame everytime.

How could I alter this to just have the JFrame refreshed?

Upvotes: 1

Views: 822

Answers (1)

durron597
durron597

Reputation: 32343

You can pretty simply do this by storing the JLabel control, and then setting the image with a method. Make sure you set the image on the Event Dispatch Thread!

I have revised the code to do this in a more stable way based on @MadProgrammer's excellent comments.

Brief summary of the issues addressed between this and the previous version:

  1. You don't want the contentPane to be a JLabel, as it makes it more difficult to add other controls later and doesn't have a layout manager. I've added a JPanel in between, and given it BorderLayout.
  2. The caller should be responsible for thread safety. I didn't do it this way originally because you didn't provide any calling code, but here, I do the Event Dispatch Thread delegation. Make sure you switch to the Event Dispatch Thread using EventQueue.invokeLater as I do here in main before calling setBackgroundImage().
  3. I'm using setSize and setPreferredSize on the JLabel so that the layout managers can properly choose good sizes for the controls, and so that frame.pack works as expected.
  4. I create the controls in the initComponents method, outside of the constructor, to make the code easier to follow and to make it easy to add more constructors later if necessary.

Here's the code:

public class NonogramSolutionJFrame extends JFrame {
  private final JLabel label;
  private final JPanel panel;

  public NonogramSolutionJFrame(BufferedImage img) {
    panel = new JPanel();
    label = new JLabel();
    initComponents(img);
  }

  private final void initComponents(BufferedImage img) {
    setTitle("Background Color for JFrame");
    setBackgroundImage(img);
    setContentPane(panel);
    panel.setLayout(new BorderLayout());
    panel.add(label, BorderLayout.CENTER);
    setLocationRelativeTo(null);
    pack();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }

  public void setBackgroundImage(final BufferedImage img) {
    label.setIcon(new ImageIcon(img));
    label.setPreferredSize(new Dimension(img.getWidth(), img.getHeight()));
  }

  public static void main(String... args) throws Exception {
    BufferedImage img = ImageIO.read(NonogramSolutionJFrame.class.getResource("/nonogram.png"));
    NonogramSolutionJFrame frame = new NonogramSolutionJFrame(img);
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        frame.setVisible(true);
      }
    });
  }
}

Using the image from your other answer, this code produces the following (on Linux):

Nonogram in a JFrame

Upvotes: 2

Related Questions