3

Let me explain the project I'm creating. I need a list of images, like a film strip, where I can drag the image from the strip to another part of the program and do things with it. That is the ultimate goal, however as of right now I'm just working my way up. I created a FilmStripItem which stores the image information and displays the thumbnail of the image. I'm trying to create a FilmStripPanel which is a JScrollPane that shows each of the thumbnails in a Y-Axis form.

Now I'm new to Java Swing and I've been banging my head against the wall for weeks trying to figure out BorderLayout, BoxLayout, GridLayout and which one would be best. On top of that I can't seem to get any of them to work the way I want. Below is the code I'm using (I got rid of the unnecessary details of the class). Basically what happens is when I run the FilmStripPanel it squishes all the images to fit the window size and no scrollbar shows up. How can I get it to display the images (80x80) without squishing them?

FilmStripItem.java

//Removed import info to save space

public class FilmStripItem extends JPanel {
    private BufferedImage testImg;
    private Image thumbnail;

    public FilmStripItem(BufferedImage img) {
        this.testImg = img;
        this.thumbnail = img.getScaledInstance(80, 80, Image.SCALE_SMOOTH);

        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
        p.setMinimumSize(new Dimension(80, 80));
        add(p);
    }

    public Image GetThumbnail() {
        return this.thumbnail;
    }

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

    public static void main(String[] ARGS) {
        try {
            //Removed look and layout info
        } catch (Exception ex) {
            Logger.getLogger("Demo").log(Level.WARNING,
                    "loading CuiLookAndFeel failed", ex);
        }
        JFrame f = new JFrame("FilmStripItem");
        final FilmStripItem fi = new FilmStripItem();
        f.setContentPane(fi);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setBounds(10, 10, 128, 128);
        f.setVisible(true);
    }
}

FilmStripPanel.java

//import info removed to save space

public class FilmStripPanel extends JPanel {
    private static final int ITEM_COUNT = 15;
    private ArrayList<FilmStripItem> filmItems;

    public FilmStripPanel() {
        JPanel p = new JPanel(new GridLayout(0, 1));
        setLayout(new GridLayout(1, 1));

        filmItems = new ArrayList<FilmStripItem>();
        JComponent[] c = new JComponent[ITEM_COUNT];

        for (int i = 0; i < ITEM_COUNT; i++) {
            FilmStripItem item = new FilmStripItem();
            filmItems.add(item);
            c[i] = item;
            p.add(c[i]);
        }

        JScrollPane sp = new JScrollPane(p, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        add(sp);
    }

    public static void main(String[] args) {
        try {
            //Removed look and feel info
        } catch (Exception ex) {
            Logger.getLogger("Demo").log(Level.WARNING,
                    "loading CuiLookAndFeel failed", ex);
        }
        JFrame f = new JFrame("FilmStrip");
        final FilmStripPanel p = new FilmStripPanel();
        f.setContentPane(p);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setBounds(10, 10, 128, 512);
        f.setVisible(true);
    }
}

I have googled for this over and over again but can't seem to figure it out for my exact problem. I'm sure there is some redundant code in here and place feel free to point it out. I could use all the help you guys could offer and any criticism as well, I'm trying to learn Java Swing for work and am failing.

Tyler Ferraro
  • 3,753
  • 1
  • 21
  • 28
  • Please check out edit to my post with runnable code example. It really is the easiest and I think the best way to do this. – Hovercraft Full Of Eels Dec 06 '11 at 21:29
  • There are some related suggestions in [Show ScrollBars in FlowLayout only when necessary](http://stackoverflow.com/questions/8141035/show-scrollbars-in-flowlayout-only-when-necessary). – trashgod Dec 06 '11 at 21:45

2 Answers2

4

To keep things easy, I would display a JList of ImageIcons myself.

Edit: you could have a Map to map the small images to the large ones (if the large ones aren't so big as to make you run out of memory), and then use the a ListSelectionListener added to the JList to get the small Icon, and then the map to get the large one. The large one can then be displayed in a JLabel.

For example:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * Source of images: Wikimedia Commons
 * http://commons.wikimedia.org/wiki/Category:Small-sized_coats_of_arms
 * @author Pete
 *
 */
@SuppressWarnings("serial")
public class FilmStripEg extends JPanel {
   public static final String WIKIMEDIA_PATH = "http://upload.wikimedia.org/" +
        "wikipedia/commons/thumb/";
   public static final String WIKIMEDIA_COMMONS_PATH = "http://upload.wikimedia.org/" +
        "wikipedia/commons/";
   public static final String[] IMAGE_PATHS = {
         "d/d8/Amriswil.jpg/98px-Amriswil.jpg",
         "e/e3/Brot-plamboz.png/102px-Brot-plamboz.png",
         "e/ef/Cerneux-pequignot.png/103px-Cerneux-pequignot.png",
         "b/b6/Cham_zg.jpg/90px-Cham_zg.jpg",
         "1/13/Chaux-du-milieu.png/102px-Chaux-du-milieu.png",
         "c/c2/Diessenhofen.GIF/99px-Diessenhofen.GIF",
         "f/f0/Cressier.gif/100px-Cressier.gif",
         "8/8c/GW-GL-Glarus_Sued.png/100px-GW-GL-Glarus_Sued.png",
         "3/3d/GW-GL-Haetzingen.png/100px-GW-GL-Haetzingen.png",
         "d/de/GW-GR-Tomils.png/100px-GW-GR-Tomils.png",
         "e/e5/Montmollin.png/101px-Montmollin.png", 
         "7/73/GW-GL-Naefels.gif/100px-GW-GL-Naefels.gif",
         "3/3d/GW-NE-Val-de-Travers.png/104px-GW-NE-Val-de-Travers.png",
         "d/d9/Herdern.gif/102px-Herdern.gif",
         "3/35/Kesswil.GIF.gif/101px-Kesswil.GIF.gif",
         "0/00/Roche-vaud.jpg/105px-Roche-vaud.jpg",
         "d/da/S-blaise.png/107px-S-blaise.png", 
         "3/3d/Vaugondr.jpg/107px-Vaugondr.jpg"};

   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;

   private DefaultListModel model = new DefaultListModel();
   private JList filmStripList = new JList(model);
   private JLabel imageLabel = new JLabel();
   private Map<ImageIcon, ImageIcon> iconMap = new HashMap<ImageIcon, ImageIcon>();

   public FilmStripEg() {
      JPanel centerPanel = new JPanel(new GridBagLayout());
      centerPanel.add(imageLabel);
      centerPanel.setBackground(Color.white);

      setLayout(new BorderLayout());
      add(new JScrollPane(filmStripList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.LINE_END);
      add(centerPanel, BorderLayout.CENTER);
      try {
         for (String smallImagePath : IMAGE_PATHS) {
            BufferedImage img = ImageIO.read(new URL(WIKIMEDIA_PATH + smallImagePath));
            ImageIcon smallIcon = new ImageIcon(img);
            model.addElement(smallIcon);

            String largeImagePath = WIKIMEDIA_COMMONS_PATH + smallImagePath.replaceAll("/\\d{1,3}px.*", "");
            BufferedImage largeImg = ImageIO.read(new URL(largeImagePath));
            ImageIcon largeIcon = new ImageIcon(largeImg);

            iconMap.put(smallIcon, largeIcon);
         }
      } catch (MalformedURLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }

      filmStripList.addListSelectionListener(new ListSelectionListener() {
         public void valueChanged(ListSelectionEvent e) {
            if (e.getValueIsAdjusting()) {
               return;
            }
            ImageIcon icon = iconMap.get(filmStripList.getSelectedValue());
            imageLabel.setIcon(icon);
         }
      });

   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      FilmStripEg mainPanel = new FilmStripEg();

      JFrame frame = new JFrame("FilmStripEg");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • I will look into this once I have the basics functioning. Thank you for the tip. – Tyler Ferraro Dec 06 '11 at 21:28
  • @Tyler: You're welcome. Again, please run the code example to see exactly what I mean. The key is to let the JList do all the hard work for you. – Hovercraft Full Of Eels Dec 06 '11 at 21:31
  • 1
    @Hovercraft: Yeah I understand where you're coming from. I think I'll use this for the actual product as it will make it easier and more intuitive when adding in the drag/drop style features. – Tyler Ferraro Dec 07 '11 at 01:21
2

Think of a scroll pane as showing a view of its contents. By default, depending on the layout, whatever you put into the scroll pane won't really know what size it should be. So it asks its parent how big it should be and the parent says "well, I'm this big, so just be this big too". If you want to make sure the scroll bars are used, you have to set a size on the item inside the scroll pane.

My suggestion would be: Create a new class, extending JPanel, to be the contents of the scroll pane (instead of p). In it, override getPreferredSize() and calculate the required height for all of the images in the panel and return that as the height. Return the maximum width of any 1 image as the width.

More details on the scroll pane are in the tutorials.

Dave
  • 5,133
  • 21
  • 27