Question

As a java non-expert, I would like to know how to change the code below to get it to work. Here is what I want to do

  • When the java code is called args contains several image filenames
  • I want to see the first image in this list
  • Then when I press a key, an index is changed, and the next image is displayed.

Using the suggestions made below, here is a compilable, running piece of code:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;


public class LoadImageApp extends Component {

    BufferedImage img;
    private static int index = 0;

    public void paint(Graphics g) {
        g.drawImage(img, 0, 0, null);
    }

    public LoadImageApp(String filename) {
        try {
            img = ImageIO.read(new File(filename));
        } catch (IOException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }
    }

    public Dimension getPreferredSize() {
        if (img == null) {
            return new Dimension(100,100);
        } else {
            return new Dimension(img.getWidth(null), img.getHeight(null));
        }
    }

    static public void changeImage(JFrame frame, String filename) {
        System.out.println("Using file "+filename);
        frame.add(new LoadImageApp(filename));
        frame.pack();
        frame.setVisible(true);
        frame.repaint();
    }

    public static void main(final String[] args) {

        char c=0;
        String filename = args[0];
        int numberImages = args.length;

        final JFrame f = new JFrame("Load Image Sample");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        f.addKeyListener(new KeyAdapter(){
                public void keyPressed(KeyEvent event) {
                    int key = event.getKeyCode();
                    if (key==81) {
                        System.exit(0);
                    } else if (key==89) {
                        System.out.println("yes");
                    } else if (key==78) {
                        System.out.println("no");
                    }
                    index += 1;
                    f.removeAll();
                    changeImage(f, args[index]);
                }
            });

        // show first image here
        changeImage(f, args[index]);

    }
}

If I use use the code like

java LoadImageApp *.jpg

it only displays the first image. I can press keys, but the image shown does not change. I want to have the image changed.

I have found revalidate() and repaint() as possible solutions. Despite frame.revalidate() does not exist at all, frame.repaint() (inside changeImage) does still not change anything. I still see the first image displayed.

Is this the right approach anyway? Is there a more elegant way?

Was it helpful?

Solution

I wrote a program to demonstrate what you asked for, here is the code:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class ImageShow {

  /** Inner class: JPanel that displays images. **/
  static class JImagePanel extends JPanel {

    protected final LinkedList<BufferedImage> images;
    protected BufferedImage currentImage;
    protected int currentIndex;

    public JImagePanel(final LinkedList<BufferedImage> images) {
      super(true);
      this.setFocusable(false);
      this.images = images;
      this.setIndex(0);
    }

    /** Has to be private to not cause issues when used in the constructor. **/
    private void setIndex(final int index) {
      if (index >= this.images.size()) {
        this.currentIndex = 0;
      } else if (index < 0) {
        this.currentIndex = this.images.size() - 1;
      } else {
        this.currentIndex = index;
      }

      this.currentImage = this.images.get(this.currentIndex);
      this.setPreferredSize(new Dimension(this.currentImage.getWidth(), this.currentImage.getHeight()));
      this.repaint();
    }

    public void shiftIndex(final int amount) {
      this.setIndex(this.currentIndex + amount);
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.drawImage(this.currentImage, 0, 0, null);
    }
  }

  public static void main(final String[] args) {
    final LinkedList<BufferedImage> images = loadImages(args);
    if (images.size() > 0) {
      final JFrame window = new JFrame("Image Show");
      window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      final JImagePanel imagePanel = new JImagePanel(images);
      window.add(imagePanel);

      window.addKeyListener(new KeyAdapter() {
        private void shiftIndex(final int amount) {
          imagePanel.shiftIndex(amount);
          window.pack();
          window.setLocationRelativeTo(null);
        }

        @Override
        public void keyReleased(KeyEvent e) {
          switch (e.getKeyCode()) {
            case KeyEvent.VK_ESCAPE:
              window.dispose();
              e.consume();
              break;
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_NUMPAD4:
              shiftIndex(-1);
              e.consume();
              break;
            case KeyEvent.VK_RIGHT:
            case KeyEvent.VK_NUMPAD6:
              shiftIndex(+1);
              e.consume();
              break;
          }
        }
      });

      window.pack();
      window.setLocationRelativeTo(null);
      window.setVisible(true);
    } else {
      System.err.println("No image could be loaded.\nPlease provide a list of image files as parameters.");
    }
  }

  private static LinkedList<BufferedImage> loadImages(final String[] filenames) {
    final LinkedList<BufferedImage> result = new LinkedList<>();
    for (String filename : filenames) {
      try {
        final File file = new File(filename);
        final BufferedImage image = ImageIO.read(file);
        if (image == null) {
          throw new IOException("Unknown image format");
        }
        result.add(image);
      } catch (IOException e) {
        System.err.println("Unable to load image \"" + filename + "\": " + e.getMessage());
      }
    }
    return result;
  }
}

Please note that this is not the most beautiful way of writing this tool, however it works.

What you should usually do:

  • Each class should be in its own .java file. The idea is to have a structure, that is easy to read even if you re-visit this code 3 years later.

  • You should not use variables from another scope like I did here with the window and imagePanel in the main function. Instead use constructors that store local variables with either the value given or a copy of the value (depending on your needs), like I did in the JImagePanel constructor.
    Whether or not you need a copy of the value depends on what you do and how much risk you are willing to take. In this example changing the image list after JImagePanel is created would potentially mess things up.

  • You should never use numbers like you did in your version of the key listener. You never know which key-code corresponds to which key! Whenever available use the provided constants or functions to get such a 'magic' number.

  • Always expect the worst when it comes to error handling. For once try to catch and handle all possible errors. For second always try to avoid potential issues. A bug you cannot make, is a bug you won't have to bother about.
    In your version the image file is loaded from disk every time a button is pressed. What happens if the image is no longer present at that moment? In my version everything is checked before-hand, and once that is done, the program cannot fail anymore (at least not when trying to switch images). ;)

  • In general: try to find a good book or online tutorial on Java for beginners. If you just hack away, you will miss all those nice things Java has already prepared for you, that will not only speed up developing a lot, it will as well prevent all bugs that you otherwise might code in.

OTHER TIPS

The keyword final indicates that the variable will not change after it has been initialized. In order to use a variable within an anonymous inner class i.e. KeyAdapter it has to be declared final.

So you need:

 public static void main(final String[] args) {

and

 final JFrame f = new JFrame("Load Image Sample");

But this will not work for index as you are planning on changing it. So I recommed declaring it as a static variable on class level i.e. outside of the function.

private static int index = 0;

This is more or less what you want. I recommend you to study Java from scratch and fill the gap you have.

    public class LoadImageApp extends JPanel {

        //Current image the Canvas shows
        private Image currentImage;

        //change the current image and repaint
        public void setCurrentImage(Image currentImage) {
                this.currentImage = currentImage;                
                repaint();
        }

        @Override
        public void paintComponent(Graphics g) {
                g.clearRect(0, 0, getWidth(), getHeight());
                if (currentImage != null)
                g.drawImage(currentImage, 0, 0, null);
        }

        @Override
        public Dimension getPreferredSize() {
                if (currentImage == null) {
                        return new Dimension(100, 100);
                } else {
                        return new Dimension(currentImage.getWidth(null), currentImage.getHeight(null));
                }
        }

        public static void main(final String[] args) throws IOException {

                if (args.length > 0){
                        final Image [] images = new Image[args.length];

                        for (int i = 0; i < images.length; i++){
                                images[i] = ImageIO.read(new File(args[i]));
                        }

                        //It is a goog practice to attach your code to AWT thread.
                        SwingUtilities.invokeLater(new Runnable() {                                
                                @Override
                                public void run() {
                                        LoadImageApp app = new LoadImageApp();
                                        JFrame f = new JFrame("Load Image Sample");

                                        f.getContentPane().setLayout(new BorderLayout());
                                        f.getContentPane().add(app, BorderLayout.CENTER);

                                        f.addKeyListener(new MyKeyAdapter(app, images));
                                        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                                        f.setVisible(true);
                                }
                        });
                }
        }        
    }

    // We create another class for the KeyAdapter
    class MyKeyAdapter extends KeyAdapter{

        //The canvas so we can tell it that the current  image has change
        private LoadImageApp canvas;

        //index of the current image
        private int index;

        //All the images
        private Image [] images;

        public MyKeyAdapter(LoadImageApp canvas, Image[] images) {
                super();
                this.canvas = canvas;
                this.images = images;                    
                rotateRight();
        }

        @Override
        public void keyPressed(KeyEvent event) {
                int key = event.getKeyCode();                    
                if (key == 81) {
                        System.exit(0);
                } else if (key == KeyEvent.VK_LEFT) {
                        rotateLeft();
                } else if (key == KeyEvent.VK_RIGHT) {
                        rotateRight();
                }
        }

        private void rotateRight() {
                //change the image in the canvas
                canvas.setCurrentImage(images[index]);                    
                //increment index
                index++;                    
                //last element + 1, set it to 0
                if (index >= images.length) index = 0;
        }

        private void rotateLeft() {
                //change the image in the canvas
                canvas.setCurrentImage(images[index]);                    
                //decrement index
                index--;                    
                //< 0, set it to last image
                if (index < 0) index = images.length - 1;
        }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top