JList Examples

All examples, except for FileTransfer use WindowUtilities.java and ExitListener.java.
WindowUtilities.java: 
import javax.swing.*;
import java.awt.*;   // For Color and Container classes.

/** A few utilities that simplify using windows in Swing. 
 *
  */

public class WindowUtilities {

  /** Tell system to use native look and feel, as in previous
   *  releases. Metal (Java) LAF is the default otherwise.
   */

  public static void setNativeLookAndFeel() {
    try {
     UIManager.setLookAndFeel(
       UIManager.getSystemLookAndFeelClassName());
    } catch(Exception e) {
      System.out.println("Error setting native LAF: " + e);
    }
  }

  public static void setJavaLookAndFeel() {
    try {
     UIManager.setLookAndFeel(
       UIManager.getCrossPlatformLookAndFeelClassName());
    } catch(Exception e) {
      System.out.println("Error setting Java LAF: " + e);
    }
  }

   public static void setMotifLookAndFeel() {
    try {
      UIManager.setLookAndFeel(
        "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
    } catch(Exception e) {
      System.out.println("Error setting Motif LAF: " + e);
    }
  }

  /** A simplified way to see a JPanel or other Container. Pops
   *  up a JFrame with specified Container as the content pane.
   */

  public static JFrame openInJFrame(Container content,
                                    int width,
                                    int height,
                                    String title,
                                    Color bgColor) {
    JFrame frame = new JFrame(title);
    frame.setBackground(bgColor);
    content.setBackground(bgColor);
    frame.setSize(width, height);
    frame.setContentPane(content);
    frame.addWindowListener(new ExitListener());
    frame.setVisible(true);
    return(frame);
  }

  /** Uses Color.white as the background color. */

  public static JFrame openInJFrame(Container content,
                                    int width,
                                    int height,
                                    String title) {
    return(openInJFrame(content, width, height,
                        title, Color.white));
  }

  /** Uses Color.white as the background color, and the
   *  name of the Container's class as the JFrame title.
   */

  public static JFrame openInJFrame(Container content,
                                    int width,
                                    int height) {
    return(openInJFrame(content, width, height,
                        content.getClass().getName(),
                        Color.white));
  }
}
**//
ExitListener.java.:
import java.awt.*;
import java.awt.event.*;

/** A listener that you attach to the top-level JFrame of
 *  your application, so that quitting the frame exits the 
 *  application.
 *
  */
public class ExitListener extends WindowAdapter {
  public void windowClosing(WindowEvent event) {
    System.exit(0);
  }
}
**//

JList Examples

* JListSimpleExample.java Illustrates creating a simple list. In this example, all the entries for the list are stored in a String array and later supplied in the JList constructor. In addition, a private class, ValueReporter, implements a ListSelectionListener to display the last entry in the list selected by the user.

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;

/** Simple JList example illustrating
 *  


     *    
    Creating a JList, which we do by passing values 
     *        directly to the JList constructor, rather than 
     *        using a ListModel, and
     *    
    Attaching a listener to determine when values change.
     *  


 */

public class JListSimpleExample extends JFrame {
  public static void main(String[] args) {
    new JListSimpleExample();
  }

  private JList sampleJList;
  private JTextField valueField;
  
  public JListSimpleExample() {
    super("Creating a Simple JList");
    WindowUtilities.setNativeLookAndFeel();
    addWindowListener(new ExitListener());
    Container content = getContentPane();

    // Create the JList, set the number of visible rows, add a
    // listener, and put it in a JScrollPane.
    String[] entries = { "Entry 1", "Entry 2", "Entry 3",
                         "Entry 4", "Entry 5", "Entry 6" };
    sampleJList = new JList(entries);
    sampleJList.setVisibleRowCount(4);
    sampleJList.addListSelectionListener(new ValueReporter());
    JScrollPane listPane = new JScrollPane(sampleJList);
    Font displayFont = new Font("Serif", Font.BOLD, 18);
    sampleJList.setFont(displayFont);

    JPanel listPanel = new JPanel();
    listPanel.setBackground(Color.white);
    Border listPanelBorder =
      BorderFactory.createTitledBorder("Sample JList");
    listPanel.setBorder(listPanelBorder);
    listPanel.add(listPane);
    content.add(listPanel, BorderLayout.CENTER);
    JLabel valueLabel = new JLabel("Last Selection:");
    valueLabel.setFont(displayFont);
    valueField = new JTextField("None", 7);
    valueField.setFont(displayFont);
    valueField.setEditable(false);
    JPanel valuePanel = new JPanel();
    valuePanel.setBackground(Color.white);
    Border valuePanelBorder =
      BorderFactory.createTitledBorder("JList Selection");
    valuePanel.setBorder(valuePanelBorder);
    valuePanel.add(valueLabel);
    valuePanel.add(valueField);
    content.add(valuePanel, BorderLayout.SOUTH);
    pack();
    setVisible(true);
  }

  private class ValueReporter implements ListSelectionListener {

    /** You get three events in many cases -- one for the 
     *  deselection of the originally selected entry, one 
     *  indicating the selection is moving, and one for the 
     *  selection of the new entry. In the first two cases, 
     *  getValueIsAdjusting returns true; thus, the test below
     *  when only the third case is of interest.
     */

    public void valueChanged(ListSelectionEvent event) {
      if (!event.getValueIsAdjusting()) {
        Object value = sampleJList.getSelectedValue();
        if (value != null) {
          valueField.setText(value.toString());
        }  
      }
    }
  }
}
**//

DefaultListModelExample.java Creates a list using a DefaultListModel. By default, a JList doesn’t permit you to directly add new entries; however, with the DefaultListModel you can add or delete entries to the model (which are reflected in the list).

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/** JList example illustrating
 *  


     *    
    The creation of a JList by creating a DefaultListModel,
     *        adding the values there, then passing that to the
     *        JList constructor.
     *    
    Adding new values at runtime, the key thing that 
     *        DefaultListModel lets you do that you can't do with
     *        a JList where you supply values directly.
     *  


 *
 */

public class DefaultListModelExample extends JFrame {
  public static void main(String[] args) {
    new DefaultListModelExample();
  }

  JList sampleJList;
  private DefaultListModel sampleModel;
  
  public DefaultListModelExample() {
    super("Creating a Simple JList");
    WindowUtilities.setNativeLookAndFeel();
    addWindowListener(new ExitListener());
    Container content = getContentPane();
   
    String[] entries = { "Entry 1", "Entry 2", "Entry 3",
                         "Entry 4", "Entry 5", "Entry 6" };
    sampleModel = new DefaultListModel();
    for(int i=0; ibefore trying to scroll
     *  to make the index visible.
     */

    public void actionPerformed(ActionEvent event) {
      int index = sampleModel.getSize();
      sampleModel.addElement("Entry " + (index+1));
      ((JComponent)getContentPane()).revalidate();
      sampleJList.setSelectedIndex(index);
      sampleJList.ensureIndexIsVisible(index);
    }
  }
}
**//

# JListCustomModel.java  Example illustrating that you can use your own custom data model (data structure) to hold the entries in a list. Uses the following classes:

* JavaLocationListModel.java A custom list model (implements ListModel interface which provides support for custom data structures) to store data for the list.
* JavaLocationCollection.java A simple collection of JavaLocation (below) objects.
* JavaLocation.java An object representing a city named Java. Defines the country where the Java city is located, along with a comment and country flag (gif image).

JListCustomModel.java:
import java.awt.*;
import javax.swing.*;

/** Simple JList example illustrating the use of a custom
 *  ListModel (JavaLocationListModel).
 *
  */

public class JListCustomModel extends JFrame {
  public static void main(String[] args) {
    new JListCustomModel();
  }

  public JListCustomModel() {
    super("JList with a Custom Data Model");
    WindowUtilities.setNativeLookAndFeel();
    addWindowListener(new ExitListener());
    Container content = getContentPane();

    JavaLocationCollection collection =
      new JavaLocationCollection();
    JavaLocationListModel listModel =
      new JavaLocationListModel(collection);
    JList sampleJList = new JList(listModel);
    Font displayFont = new Font("Serif", Font.BOLD, 18);
    sampleJList.setFont(displayFont);
    content.add(sampleJList);

    pack();
    setVisible(true);
  }
}
**//
# JavaLocationListModel.java:

import javax.swing.*;
import javax.swing.event.*;

/** A simple illustration of writing your own ListModel.
 *  Note that if you wanted the user to be able to add and
 *  remove data elements at runtime, you should start with
 *  AbstractListModel and handle the event reporting part.
 *
 */

public class JavaLocationListModel implements ListModel {
  private JavaLocationCollection collection;
  
  public JavaLocationListModel(JavaLocationCollection collection) {
    this.collection = collection;
  }

  public Object getElementAt(int index) {
    return(collection.getLocations()[index]);
  }

  public int getSize() {
    return(collection.getLocations().length);
  }

  public void addListDataListener(ListDataListener l) {}

  public void removeListDataListener(ListDataListener l) {}
}

**//

JavaLocationCollection.java:
/** A simple collection that stores multiple JavaLocation
 *  objects in an array and determines the number of
 *  unique countries represented in the data.
 *
 */

public class JavaLocationCollection {
  private static JavaLocation[] defaultLocations =
    { new JavaLocation("Belgium",
                       "near Liege",
                       "flags/belgium.gif"),
      new JavaLocation("Brazil",
                       "near Salvador",
                       "flags/brazil.gif"),
      new JavaLocation("Colombia",
                       "near Bogota",
                       "flags/colombia.gif"),
      new JavaLocation("Indonesia",
                       "main island",
                       "flags/indonesia.gif"),
      new JavaLocation("Jamaica",
                       "near Spanish Town",
                       "flags/jamaica.gif"),
      new JavaLocation("Mozambique",
                       "near Sofala",
                       "flags/mozambique.gif"),
      new JavaLocation("Philippines",
                       "near Quezon City",
                       "flags/philippines.gif"),
      new JavaLocation("Sao Tome",
                       "near Santa Cruz",
                       "flags/saotome.gif"),
      new JavaLocation("Spain",
                       "near Viana de Bolo",
                       "flags/spain.gif"),
      new JavaLocation("Suriname",
                       "near Paramibo",
                       "flags/suriname.gif"),
      new JavaLocation("United States",
                       "near Montgomery, Alabama",
                       "flags/usa.gif"),
      new JavaLocation("United States",
                       "near Needles, California",
                       "flags/usa.gif"),
      new JavaLocation("United States",
                       "near Dallas, Texas",
                       "flags/usa.gif")
    };

  private JavaLocation[] locations;
  private int numCountries;

  public JavaLocationCollection(JavaLocation[] locations) {
    this.locations = locations;
    this.numCountries = countCountries(locations);
  }
  
  public JavaLocationCollection() {
    this(defaultLocations);
  }

  public JavaLocation[] getLocations() {
    return(locations);
  }

  public int getNumCountries() {
    return(numCountries);
  }

  // Count the number of unique countries in the data.
  // Assumes the list is sorted by country name
  private int countCountries(JavaLocation[] locations) {
    int n = 0;
    String currentCountry, previousCountry = "None";
    for(int i=0;i
 *  Note that this method can get called lots and lots of times
 *  as you click on entries. We don't want to keep generating 
 *  new ImageIcon objects, so we make a Hashtable that associates 
 *  previously displayed values with icons, reusing icons for 
 *  entries that have been displayed already.
 *  


 *  Note that in the first release of JDK 1.2, the default 
 *  renderer  has a bug: the renderer doesn't clear out icons for 
 *  later entries. So if you mix plain strings and ImageIcons in 
 *  your JList, the plain strings still get an icon. The 
 *  call below clears the old icon when the value is not a 
 *  JavaLocation.
 *
 
 */

public class JavaLocationRenderer extends 
                                  DefaultListCellRenderer {
  private Hashtable iconTable = new Hashtable();
  
  public Component getListCellRendererComponent(JList list,
                                                Object value,
                                                int index,
                                                boolean isSelected,
                                                boolean hasFocus) {
    // First build the label containing the text, then 
    // later add the image.
    JLabel label =
      (JLabel)super.getListCellRendererComponent(list,
                                                 value,
                                                 index,
                                                 isSelected,
                                                 hasFocus);
    if (value instanceof JavaLocation) {
      JavaLocation location = (JavaLocation)value;
      ImageIcon icon = (ImageIcon)iconTable.get(value);
      if (icon == null) {
        icon = new ImageIcon(location.getFlagFile());
        iconTable.put(value, icon);
      }
      label.setIcon(icon);
    } else {
      // Clear old icon; needed in 1st release of JDK 1.2.
      label.setIcon(null); 
    }
    return(label);
  }
}
**//
# JavaLocationListModel.java A custom list model (implements ListModel interface) to store data for a list.
# JavaLocationCollection.java A simple collection of JavaLocation (below) objects.
# JavaLocation.java An object representing a city named Java. Defines the country where the Java 
city is located, along with a comment and country flag

Position circles down the diagonal so that their borders

import java.awt.*;
import java.applet.Applet;

/** Position circles down the diagonal so that their borders
 *  just touch. Illustrates that AWT components are 
 *  rectangular and opaque.
  */

public class CircleTest2 extends Applet {
  public void init() {
    setBackground(Color.lightGray);
    setLayout(null); // Turn off layout manager.
    Circle circle;
    int radius = getSize().width/6;
    int deltaX = round(2.0 * (double)radius / Math.sqrt(2.0));
    for (int x=radius; x<6*radius; x=x+deltaX) {
      circle = new Circle(Color.black, radius);
      add(circle);
      circle.setCenter(x, x);
    }
  }

  private int round(double num) {
    return((int)Math.round(num));
  }
}
*********************************
Circle.java
*********************************
import java.awt.*;

/** A Circle component built using a Canvas. 
 *
  */

public class Circle extends Canvas {
  private int width, height;        
        
  public Circle(Color foreground, int radius) {
    setForeground(foreground);
    width = 2*radius;
    height = 2*radius;
    setSize(width, height);
  }

  public void paint(Graphics g) {
    g.fillOval(0, 0, width, height);
  }

  public void setCenter(int x, int y) {
    setLocation(x - width/2, y - height/2);
  }
}
*****************************

Insert three circles into an Applet using FlowLayout

import java.awt.*;
import java.applet.Applet;

/** Insert three circles into an Applet using FlowLayout. 
 *
 */

public class CircleTest extends Applet {
  public void init() {
    setBackground(Color.lightGray);
    add(new Circle(Color.white, 30));
    add(new Circle(Color.gray, 40));
    add(new Circle(Color.black, 50));
  }
}

Message.java Applet that reads customization parameters from an HTML file

*******************
Message.java Applet that reads customization parameters from an HTML file
*******************
import java.applet.Applet;
import java.awt.*;

****************
 
public class Message extends Applet {
  private int fontSize;
  private String message;
  
  public void init() {
    setBackground(Color.black);
    setForeground(Color.white);
    
    // Base font size on window height.
    fontSize = getSize().height - 10;
    
    setFont(new Font("SansSerif", Font.BOLD, fontSize));

    // Read heading message from PARAM entry in HTML.
    message = getParameter("MESSAGE");
  }

  public void paint(Graphics g) {
    if (message != null) {
      g.drawString(message, 5, fontSize+5);
    }
  }
}
*******************

Accesses methods in a Ship2 object

*********************************************
Test2.java Accesses methods in a Ship2 object
*********************************************

// Give the ship public move and printLocation methods.

class Ship2 {
  public double x=0.0, y=0.0, speed=1.0, direction=0.0;
  public String name = "UnnamedShip";

  private double degreesToRadians(double degrees) {
    return(degrees * Math.PI / 180.0);
  }

  public void move() {
    double angle = degreesToRadians(direction);
    x = x + speed * Math.cos(angle);
    y = y + speed * Math.sin(angle);
  }

  public void printLocation() {
    System.out.println(name + " is at " +
                       "(" + x + "," + y + ").");
  }
}

public class Test2 {
  public static void main(String[] args) {
    Ship2 s1 = new Ship2();
    s1.name = "Ship1";
    Ship2 s2 = new Ship2();
    s2.direction = 135.0; // Northwest
    s2.speed = 2.0;
    s2.name = "Ship2";
    s1.move();
    s2.move();
    s1.printLocation();
    s2.printLocation();
  }
}
**********************

Placement of buttons in a BoxLayout using rigid areas, struts, and glue

###############
InvisibleComponentTest.java Placement of buttons in a BoxLayout using rigid areas, struts, and glue
###############
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

/** Example of using rigid areas, struts, and glue to
 *  produce the effect of invisible components.
 *
 ******************

public class InvisibleComponentTest extends JPanel {
  Component spacer;

  public InvisibleComponentTest() {
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

    // Place a rigid invisible component 25 pixels wide and
    // 75 pixels tall between the two buttons
    JPanel p1= new JPanel();
    spacer = Box.createRigidArea(new Dimension(20,75));
    setUpPanel(p1, "Rigid Area - 20x75 pixels", spacer);

    // Separate two buttons by a 60-pixel horizontal strut
    JPanel p2= new JPanel();
    spacer = Box.createHorizontalStrut(60);
    setUpPanel(p2, "Horizontal Strut - 60 pixels", spacer);

    // Horizontal glue in FlowLayout - not useful
    JPanel p3= new JPanel();
    spacer = Box.createHorizontalGlue();
    setUpPanel(p3, "Horizontal Glue - FlowLayout", spacer);

    // Add glue to fill all remaining horizontal space between
    // the two buttons. Glue not supported by default FlowLayout
    // of JPanel.  Change layout of JPanel to BoxLayout.
    JPanel p4= new JPanel();
    p4.setLayout(new BoxLayout(p4,BoxLayout.X_AXIS));
    spacer = Box.createHorizontalGlue();
    setUpPanel(p4, "Horizontal Glue - BoxLayout", spacer);

    add(p1);
    add(p2);
    add(p3);
    add(p4);
  }

  // Helper to set the border and add components
  private void setUpPanel(JPanel p, String title,
                          Component spacer) {
    p.setBorder(BorderFactory.createTitledBorder(
                       BorderFactory.createEtchedBorder(),title,
                       TitledBorder.TOP,TitledBorder.CENTER));
    p.setBackground(Color.white);
    p.add(new JButton("Left"));
    p.add(spacer);
    p.add(new JButton("Right"));
  }

  public static void main(String[] args) {
    String title = "Using Invisible Components";
    WindowUtilities.setNativeLookAndFeel();
    WindowUtilities.openInJFrame(new InvisibleComponentTest(),
                                 350, 325, title);
  }
}

Adds typing to the freehand drawing.

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

/** A better whiteboard that lets you enter
 *  text in addition to freehand drawing.
 *  


 ******************

public class Whiteboard extends SimpleWhiteboard {
  protected FontMetrics fm;

  public void init() {
    super.init();
    Font font = new Font("Serif", Font.BOLD, 20);
    setFont(font);
    fm = getFontMetrics(font);
    addKeyListener(new CharDrawer());
  }

  private class CharDrawer extends KeyAdapter {
    // When user types a printable character,
    // draw it and shift position rightwards.
    
    public void keyTyped(KeyEvent event) {
      String s = String.valueOf(event.getKeyChar());
      getGraphics().drawString(s, lastX, lastY);
      record(lastX + fm.stringWidth(s), lastY);
    }
  }
}

java Nested container where the top-level panels are positioned by hand

######################
ButtonCol.java Nested container where the top-level panels are positioned by hand
######################
import java.applet.Applet;
import java.awt.*;

/** An example of a layout performed manually. The top-level
 *  panels are positioned by hand, after you determine the size
 *  of the applet. Since applets can't be resized in most
 *  browsers, setting the size once when the applet is created
 *  is sufficient.
 *
 ***************************

public class ButtonCol extends Applet {
  public void init() {
    setLayout(null);
    int width1 = getSize().width*4/10,
        width2 = getSize().width - width1,
        height = getSize().height;
    Panel buttonPanel = new Panel();
    buttonPanel.setBounds(0, 0, width1, height);
    buttonPanel.setLayout(new GridLayout(6, 1));
    buttonPanel.add(new Label("Buttons", Label.CENTER));
    buttonPanel.add(new Button("Button One"));
    buttonPanel.add(new Button("Button Two"));
    buttonPanel.add(new Button("Button Three"));
    buttonPanel.add(new Button("Button Four"));
    buttonPanel.add(new Button("Button Five"));
    add(buttonPanel);
    Panel everythingElse = new Panel();
    everythingElse.setBounds(width1+1, 0, width2, height);
    everythingElse.add(new Label("Everything Else"));
    add(everythingElse);
  }
}

Layout of complicated GUI by taking advantage of nested containers

#################
NestedLayout.java Layout of complicated GUI by taking advantage of nested containers. Uses WindowUtilities.java and ExitListener.java. 
##################
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;

/** An example demonstrating the use of nested containers
 *  to lay out the components. See GridBagTest.java for
 *  implementation by a single layout manager, GridBagLayout.
 *
 *******************
 */

public class NestedLayout extends JPanel {

  private JTextArea textArea;
  private JButton bSaveAs, bOk, bExit;
  private JTextField fileField;

  public NestedLayout() {

    setLayout(new BorderLayout(2,2));
    setBorder(BorderFactory.createEtchedBorder());

    textArea = new JTextArea(12,40);  // 12 rows, 40 cols
    bSaveAs = new JButton("Save As");
    fileField = new JTextField("C:\\Document.txt");
    bOk = new JButton("OK");
    bExit = new JButton("Exit");

    add(textArea,BorderLayout.CENTER);

    // Set up buttons and textfield in bottom panel.
    JPanel bottomPanel = new JPanel();
    bottomPanel.setLayout(new GridLayout(2,1));

    JPanel subPanel1 = new JPanel();
    JPanel subPanel2 = new JPanel();
    subPanel1.setLayout(new BorderLayout());
    subPanel2.setLayout(new FlowLayout(FlowLayout.RIGHT,2,2));

    subPanel1.add(bSaveAs,BorderLayout.WEST);
    subPanel1.add(fileField,BorderLayout.CENTER);
    subPanel2.add(bOk);
    subPanel2.add(bExit);

    bottomPanel.add(subPanel1);
    bottomPanel.add(subPanel2);

    add(bottomPanel,BorderLayout.SOUTH);
  }

  public static void main(String[] args) {
    WindowUtilities.setNativeLookAndFeel();
    JFrame frame = new JFrame("Nested Containers");
    frame.setContentPane(new NestedLayout());
    frame.addWindowListener(new ExitListener());
    frame.pack();
    frame.setVisible(true);
  }
}
##########################

A demo providing multiple buttons to select a playing card-A Panel, using CardLayout control which of four possible subpanels, holding a different card, to display

#######################
# CardDemo.java A demo providing multiple buttons to select a playing card. A Panel, using CardLayout control which of four possible subpanels, holding a different card, to display.Uses the following class and images:
    * CardPanel.java A Panel that displays a playing card.
    * ImageLabel.java A Canvas for displaying images.
    * Ace.gif, King.gif, Queen.gif, Jack.gif.
#####################
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

/** An example of CardLayout. The right side of the window holds
 *  a Panel that uses CardLayout to control four possible
 *  subpanels (each of which is a CardPanel that shows a
 *  picture of a playing card). The buttons on the left side
 *  of the window manipulate the "cards" in this layout by
 *  calling methods in the right-hand panel's layout manager.
 *
 *********************************

public class CardDemo extends Applet implements ActionListener {
  private Button first, last, previous, next;
  private String[] cardLabels = { "Jack","Queen","King","Ace" };
  private CardPanel[] cardPanels = new CardPanel[4];
  private CardLayout layout;
  private Panel cardDisplayPanel;

  public void init() {
    setBackground(Color.white);
    setLayout(new BorderLayout());
    addButtonPanel();
    addCardDisplayPanel();
  }

  private void addButtonPanel() {
    Panel buttonPanel = new Panel();
    buttonPanel.setLayout(new GridLayout(9, 1));
    Font buttonFont = new Font("SansSerif", Font.BOLD, 18);
    buttonPanel.setFont(buttonFont);
    for(int i=0; inot use CardLayout. Rather, instances of CardPanel
 *  are contained in another window used in the CardDemo
 *  example. It is this enclosing window that uses CardLayout
 *  to manipulate which CardPanel it shows.
 *
 *************************

public class CardPanel extends Panel {
  private Label name;
  private ImageLabel picture;

  public CardPanel(String cardName,
                   URL directory, String imageFile) {
    setLayout(new BorderLayout());
    name = new Label(cardName, Label.CENTER);
    name.setFont(new Font("SanSerif", Font.BOLD, 50));
    add(name, BorderLayout.NORTH);
    picture = new ImageLabel(directory, imageFile);
    Panel picturePanel = new Panel();
    picturePanel.add(picture);
    add(picturePanel, BorderLayout.CENTER);
    setSize(getPreferredSize());
  }

  public Label getLabel() {
    return(name);
  }

  public ImageLabel getImageLabel() {
    return(picture);
  }
}
*******************
ImageLabel.java A Canvas for displaying images.
*******************
import java.awt.*;
import java.net.*; 

/** A class for displaying images. It places the Image
 *  into a canvas so that it can moved around by layout
 *  managers, will get repainted automatically, etc.
 *  No mouseXXX or action events are defined, so it is
 *  most similar to the Label Component.
 *  


 *  By default, with FlowLayout the ImageLabel takes
 *  its minimum size (just enclosing the image). The
 *  default with BorderLayout is to expand to fill
 *  the region in width (North/South), height
 *  (East/West) or both (Center). This is the same
 *  behavior as with the builtin Label class. If you
 *  give an explicit setSize or
 *  setBounds call before adding the
 *  ImageLabel to the Container, this size will
 *  override the defaults.
 *  


 *  Here is an example of its use:
 *  


 *  

 *  public class ShowImages extends Applet {
 *    private ImageLabel image1, image2;
 *
 *    public void init() {
 *      image1 = new ImageLabel(getCodeBase(),
 *                              "some-image.gif");
 *      image2 = new ImageLabel(getCodeBase(),
 *                              "other-image.jpg");
 *      add(image1);
 *      add(image2);
 *    }
 *  }
 *  


 *
  */

public class ImageLabel extends Canvas {
  // Instance variables.
  
  // The actual Image drawn on the canvas. 
  private Image image;

  // A String corresponding to the URL of the image you will 
  // get if you call the constructor with no arguments.
  private static String defaultImageString
    = "http://java.sun.com/lib/images/" +
      "logo.java.color-transp.55x60.gif";

  // The URL of the image. But sometimes we will use an existing
  // image object (e.g. made by createImage) for which this info 
  // will not be available, so a default string is used here.
  private String imageString = "";

  // Turn this on to get verbose debugging messages. 
  private boolean debug = false;

  /** Amount of extra space around the image. */
  
  private int border = 0;

  /** If there is a non-zero border, what color should it be? 
   *  Default is to use the background color of the Container.
   */
   
  private Color borderColor = null;
  
  // Width and height of the Canvas. This is the
  //  width/height of the image plus twice the border.
  private int width, height;

  /** Determines if it will be sized automatically. If the user
   *  issues a setSize() or setBounds()call before adding the 
   *  label to the Container, or if the LayoutManager resizes 
   *  before drawing (as with BorderLayout), then those sizes
   *  override the default, which is to make the label the same 
   *  size as the image it holds (after reserving space for the 
   *  border, if any). This flag notes this, so subclasses that
   *  override ImageLabel need to check this flag, and if it is 
   *  true, and they draw modified image, then they need to draw 
   *  them based on the width height variables, not just blindly 
   *  drawing them full size.
   */
   
  private boolean explicitSize = false;
  private int explicitWidth=0, explicitHeight=0;
  
  // The MediaTracker that can tell if image has been loaded 
  // before trying to paint it or setSize based on its size.
  private MediaTracker tracker;
  
  // Used by MediaTracker to be sure image is loaded before 
  // paint & setSize, since you can't find out the size until it
  // is done loading.
  private static int lastTrackerID=0;
  private int currentTrackerID;
  private boolean doneLoading = false;

  private Container parentContainer;

  /** Create an ImageLabel with the default image.
   *
   * @see #getDefaultImageString
   * @see #setDefaultImageString
   */
  // Remember that the funny "this()" syntax calls
  // constructor of same class
  public ImageLabel() {
    this(defaultImageString);
  }
  
  /** Create an ImageLabel using the image at URL
   *  specified by the string.
   *
   * @param imageURLString A String specifying the
   *   URL of the image.
   */
   
  public ImageLabel(String imageURLString) {
    this(makeURL(imageURLString));
  }

  /** Create an ImageLabel using the image at URL
   *  specified.
   *
   * @param imageURL The URL of the image.
   */
   
  public ImageLabel(URL imageURL) {
    this(loadImage(imageURL));
    imageString = imageURL.toExternalForm();
  }

  /** Create an ImageLabel using the image in the file
   *  in the specified directory.
   *
   * @param imageDirectory Directory containing image
   * @param file Filename of image
   */
   
  public ImageLabel(URL imageDirectory, String file) {
    this(makeURL(imageDirectory, file));
    imageString = file;
  }
  
  /** Create an ImageLabel using the image specified. The other
   *  constructors eventually call this one, but you may want 
   *  to call it directly if you already have an image (e.g. 
   *  created through createImage).
   *
   * @param image The image
   */
   
  public ImageLabel(Image image) {
    this.image = image;
    tracker = new MediaTracker(this);
    currentTrackerID = lastTrackerID++;
    tracker.addImage(image, currentTrackerID);
  }

  /** Makes sure that the Image associated with the Canvas is 
   *  done loading before returning, since loadImage spins off
   *  a separate thread to do the loading. Once you get around 
   *  to drawing the image, this will make sure it is loaded,
   *  waiting if not. The user does not need to call this at 
   *  all, but if several ImageLabels are used in the same 
   *  Container, this can cause several repeated layouts, so 
   *  users might want to explicitly call this themselves before
   *  adding the ImageLabel to the Container. Another 
   *  alternative is to start asynchronous loading by calling 
   *  prepareImage on the ImageLabel's image (see getImage). 
   *
   * @param doLayout Determines if the Container should be 
   *   re-laid out after you are finished waiting. This 
   *   should be true when called from user functions, 
   *   but is set to false when called from getPreferredSize 
   *   to avoid an infinite loop. This is needed when using 
   *   BorderLayout, which calls getPreferredSize
   *   before calling paint.
   */
   
  public void waitForImage(boolean doLayout) {
    if (!doneLoading) {
      debug("[waitForImage] - Resizing and waiting for "
            + imageString);
      try { tracker.waitForID(currentTrackerID); } 
      catch (InterruptedException ie) {} 
      catch (Exception e) { 
        System.out.println("Error loading "
                           + imageString + ": "
                           + e.getMessage()); 
        e.printStackTrace(); 
      } 
      if (tracker.isErrorID(0)) 
        new Throwable("Error loading image "
                      + imageString).printStackTrace();
      doneLoading = true;
      if (explicitWidth != 0)
        width = explicitWidth;
      else
        width = image.getWidth(this) + 2*border;
      if (explicitHeight != 0)
        height = explicitHeight;
      else
        height = image.getHeight(this) + 2*border;
      setSize(width, height);
      debug("[waitForImage] - " + imageString + " is "
            + width + "x" + height + ".");

      // If no parent, you are OK, since it will have
      // been resized before being added. But if
      // parent exists, you have already been added,
      // and the change in size requires re-layout. 
      if (((parentContainer = getParent()) != null)
          && doLayout) {
        setBackground(parentContainer.getBackground());
        parentContainer.doLayout();
      }
    }
  }

  /** Moves the image so that it is centered at
   *  the specified location, as opposed to the setLocation
   *  method of Component which places the top left
   *  corner at the specified location.
   *  


   *  Note: The effects of this could be undone
   *  by the LayoutManager of the parent Container, if
   *  it is using one. So this is normally only used
   *  in conjunction with a null LayoutManager.
   *
   * @param x The X coord of center of the image
   *          (in parent's coordinate system)
   * @param y The Y coord of center of the image
   *          (in parent's coordinate system)
   * @see java.awt.Component#setLocation
   */

  public void centerAt(int x, int y) {
    debug("Placing center of " + imageString + " at ("
          + x + "," + y + ")");
    setLocation(x - width/2, y - height/2); 
  }

  /** Determines if the x and y (in the ImageLabel's
   *  own coordinate system) is inside the
   *  ImageLabel. Put here because Netscape 2.02 has
   *  a bug in which it doesn't process contains() and
   *  locate() tests correctly. 
   */
   
  public synchronized boolean contains(int x, int y) {
    return((x >= 0) && (x <= width)
           && (y >= 0) && (y <= height));
  }
  
  /** Draws the image. If you override this in a
   *  subclass, be sure to call super.paint.
   */
   
  public void paint(Graphics g) {
    if (!doneLoading)
      waitForImage(true);
    else {
      if (explicitSize)
        g.drawImage(image, border, border,
                    width-2*border, height-2*border,
                    this);
      else
        g.drawImage(image, border, border, this);
      drawRect(g, 0, 0, width-1, height-1,
               border, borderColor);
    }
  }

  /** Used by layout managers to calculate the usual
   *  size allocated for the Component. Since some
   *  layout managers (e.g. BorderLayout) may
   *  call this before paint is called, you need to
   *  make sure that the image is done loading, which
   *  will force a setSize, which determines the values
   *  returned.
   */
  public Dimension getPreferredSize() {
    if (!doneLoading)
      waitForImage(false);
    return(super.getPreferredSize());
  }

  /** Used by layout managers to calculate the smallest
   *  size allocated for the Component. Since some
   *  layout managers (e.g. BorderLayout) may
   *  call this before paint is called, you need to
   *  make sure that the image is done loading, which
   *  will force a setSize, which determines the values
   *  returned.
   */
   public Dimension getMinimumSize() {
     if (!doneLoading)
       waitForImage(false);
     return(super.getMinimumSize());
   }
  
  // LayoutManagers (such as BorderLayout) might call
  // setSize or setBounds with only 1 dimension of
  // width/height non-zero. In such a case, you still
  // want the other dimension to come from the image
  // itself.

  /** Resizes the ImageLabel. If you don't setSize the
   *  label explicitly, then what happens depends on
   *  the layout manager. With FlowLayout, as with
   *  FlowLayout for Labels, the ImageLabel takes its
   *  minimum size, just enclosing the image. With
   *  BorderLayout, as with BorderLayout for Labels,
   *  the ImageLabel is expanded to fill the
   *  section. Stretching GIF/JPG files does not always
   *  result in clear looking images. So just as
   *  with builtin Labels and Buttons, don't
   *  use FlowLayout if you don't want the Buttons to
   *  get resized. If you don't use any
   *  LayoutManager, then the ImageLabel will also
   *  just fit the image.
   *  


   *  Note that if you setSize explicitly, you must do
   *  it before the ImageLabel is added to the
   *  Container. In such a case, the explicit size
   *  overrides the image dimensions.
   *
   * @see #setBounds
   */
   
  public void setSize(int width, int height) {
    if (!doneLoading) {
      explicitSize=true;
      if (width > 0)
        explicitWidth=width;
      if (height > 0)
        explicitHeight=height;
    }
    super.setSize(width, height);
  }

  /** Resizes the ImageLabel. If you don't setSize the
   *  label explicitly, then what happens depends on
   *  the layout manager. With FlowLayout, as with
   *  FlowLayout for Labels, the ImageLabel takes its
   *  minimum size, just enclosing the image. With
   *  BorderLayout, as with BorderLayout for Labels,
   *  the ImageLabel is expanded to fill the
   *  section. Stretching GIF/JPG files does not always
   *  result in clear looking images. So just as
   *  with builtin Labels and Buttons, don't
   *  use FlowLayout if you don't want the Buttons to
   *  get resized. If you don't use any
   *  LayoutManager, then the ImageLabel will also
   *  just fit the image.
   *  


   *  Note that if you setSize explicitly, you must do
   *  it before the ImageLabel is added to the
   *  Container. In such a case, the explicit size
   *  overrides the image dimensions.
   *
   * @see #setSize
   */
   
  public void setBounds(int x, int y,
                      int width, int height) {
    if (!doneLoading) {
      explicitSize=true;
      if (width > 0)
        explicitWidth=width;
      if (height > 0)
        explicitHeight=height;
    }
    super.setBounds(x, y, width, height);
  }
  
  // You can't just set the background color to
  // the borderColor and skip drawing the border,
  // since it messes up transparent gifs. You
  // need the background color to be the same as
  // the container.
  
  /** Draws a rectangle with the specified OUTSIDE
   *  left, top, width, and height.
   *  Used to draw the border.
   */
   
  protected void drawRect(Graphics g,
                          int left, int top,
                          int width, int height,
                          int lineThickness,
                          Color rectangleColor) {
    g.setColor(rectangleColor);
    for(int i=0; i