JMU
Cell Renderers
in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Motivation
Generic Renderers
Existing Hooks
Creating a Renderer
Using an Existing Component

The Renderer

javaexamples/cellrenderer/ColorCellRenderer.java
import java.awt.*;
import javax.swing.*;

/**
 * A cell renderer that can be used for cells containing Color objects.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ColorCellRenderer extends JLabel implements ListCellRenderer<Color> 
{
  private static final long serialVersionUID = 1L;
  private static final Font FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12);
  

  /**
   * Default Constructor.
   */
  public ColorCellRenderer() 
  {
    super();
    setOpaque(true);
    setFont(FONT);
  }

  /**
   * Get the component that does the rendering.
   *
   * @param table      The JTable that is being rendered
   * @param value      The value of the cell to be rendered
   * @param isSelected true if the cell is to be rendered with highlighting
   * @param hasFocise  true if the cell has the focus
   * @param row        The row of the cell being rendered
   * @param column     The column of the cell being rendered
   * @return           The Component to use as the renderer
   */
  public Component getListCellRendererComponent(JList<? extends Color> list, Color value, int index, boolean isSelected, boolean hasFocus) 
  {
    int rr = value.getRed();
    int gg = value.getGreen();
    int bb = value.getBlue();
    setText(String.format("%3d,%3d,%3d", rr, gg, bb));

    setForeground(value);
    
    if (isSelected) setBorder(BorderFactory.createLineBorder(value));
    else            setBorder(BorderFactory.createLineBorder(getBackground()));
    
    return this;
  }
}
        
Using an Existing Component (cont.)

An Application

javaexamples/cellrenderer/ColorCellRendererDemo.java
import java.awt.*;
import javax.swing.*;

/**
 * A demonstration of a simple ListCellRenderer.
 * 
 * @author Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ColorCellRendererDemo implements Runnable
{
  /**
   * Create the GUI.
   * 
   * @param args The command line arguments (which are ignored)
   * @throws Exception if something goes wrong
   */
 public static void main(String[] args) throws Exception
  {
    SwingUtilities.invokeAndWait(new ColorCellRendererDemo());
  }
  
 /**
  * The code to execute in the event dispatch thread.
  */
 public void run()
  {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(640,  480);
    
    JPanel contentPane = (JPanel)f.getContentPane();

    Color[] colors = {Color.RED, Color.GREEN, Color.BLUE};
    JList<Color> list = new JList<Color>(colors);
    list.setCellRenderer(new ColorCellRenderer());
    
    contentPane.add(list);
    f.setVisible(true);
  }
}
        
Using a Custom Container

A Supporting Class

javaexamples/cellrenderer/Fraction.java
/**
 * A very simple immutable Fraction class that is used in the FractionLabel
 * class.
 * 
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Fraction
{
  private int denominator, numerator;
  
  /**
   * Explicit Value Constructor.
   * 
   * @param numerator   The numerator
   * @param denominator The denominator
   */
  public Fraction(int numerator, int denominator)
  {
    this.numerator = numerator;
    this.denominator = denominator;
  }
  
  /**
   * Get the denominator.
   * 
   * @return The denominator
   */
  public int getDenominator()
  {
    return denominator;
  }
  
  /**
   * Get the numerator.
   * 
   * @return  The numerator
   */
  public int getNumerator()
  {
    return numerator;
  }
}
        
Using a Custom Container (cont.)

The Component

javaexamples/cellrenderer/FractionLabel.java
import java.awt.*;
import javax.swing.*;

/**
 * A GUI Component that can contain Fraction objects.
 * 
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class FractionLabel extends JPanel
{
  private static final long serialVersionUID = 1L;

  private Fraction fraction;
  private JLabel   denominatorLabel, numeratorLabel;
  
  /**
   * Default Constructor.
   */
  public FractionLabel()
  {
    super();
    JPanel west = new JPanel();
    west.setLayout(new GridLayout(2,1));
    numeratorLabel = new JLabel("");
    numeratorLabel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, numeratorLabel.getForeground()));
    west.add(numeratorLabel);
    
    denominatorLabel = new JLabel("");
    west.add(denominatorLabel);
    
    setLayout(new BorderLayout());
    add(west, BorderLayout.WEST);
  }

  /**
   * Get the Fraction that is on this Component.
   * 
   * @return  The Fraction
   */
  public Fraction getFraction()
  {
    return fraction;
  }

  /**
   * Set the Fraction that is on this Component.
   * 
   * @param f  The Fraction
   */
  public void setFraction(Fraction f)
  {
    this.fraction = f;
    numeratorLabel.setText(String.format("%d", f.getNumerator()));
    denominatorLabel.setText(String.format("%d", f.getDenominator()));
  }
}
        
Using a Custom Container (cont.)

The Renderer

javaexamples/cellrenderer/FractionCellRenderer.java
import java.awt.*;
import javax.swing.*;

/**
 * A cell renderer that can be used for cells containing Fraction objects.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class FractionCellRenderer extends FractionLabel implements ListCellRenderer<Fraction> 
{
  private static final long serialVersionUID = 1L;
  

  /**
   * Default Constructor.
   */
  public FractionCellRenderer() 
  {
    super();
    setOpaque(true);
  }

  /**
   * Get the component that does the rendering.
   *
   * @param table      The JTable that is being rendered
   * @param value      The value of the cell to be rendered
   * @param isSelected true if the cell is to be rendered with highlighting
   * @param hasFocus   true if the cell has the focus
   * @param row        The row of the cell being rendered
   * @param column     The column of the cell being rendered
   * @return           The Component to use as the renderer
   */
  public Component getListCellRendererComponent(JList<? extends Fraction> list, Fraction value, int index, boolean isSelected, boolean hasFocus) 
  {
    setFraction(value);
    
    if (isSelected) setBorder(BorderFactory.createLineBorder(getForeground()));
    else            setBorder(BorderFactory.createLineBorder(getBackground()));
    
    return this;
  }
}
        
Using a Custom Container (cont.)

An Application

javaexamples/cellrenderer/FractionCellRendererDemo.java
import javax.swing.*;

/**
 * A demonstration of a simple ListCellRenderer.
 * 
 * @author Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class FractionCellRendererDemo implements Runnable
{
  /**
   * Create the GUI.
   * 
   * @param args The command line arguments (which are ignored)
   * @throws Exception if something goes wrong
   */
 public static void main(String[] args) throws Exception
  {
    SwingUtilities.invokeAndWait(new FractionCellRendererDemo());
  }
  
 /**
  * The code to execute in the event dispatch thread.
  */
 public void run()
  {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(640,  480);
    
    JPanel contentPane = (JPanel)f.getContentPane();

    Fraction[] fractions = {new Fraction(1,2), new Fraction(3,4), new Fraction(13,16)};
    JList<Fraction> list = new JList<Fraction>(fractions);
    list.setCellRenderer(new FractionCellRenderer());
    
    contentPane.add(list);
    f.setVisible(true);
  }
}
        
Using a Custom Component

The Component

javaexamples/cellrenderer/ShapeLabel.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;

import javax.swing.*;

public class ShapeLabel extends JLabel
{
  private static final long serialVersionUID = 1L;
  
  private static final Dimension MINIMUM_SIZE = new Dimension(50,50);
  private static final Color GOLD   = new Color(173, 156, 101);
  private static final Color PURPLE = new Color( 69,   0, 132);
  
  private Shape shape;

  /**
   * Default Constructor.
   */
  public ShapeLabel()
  {
    super();
    shape = null;
  }
  
  /**
   * Set the Shape.
   * 
   * @param shape  The shape.
   */
  public void setShape(Shape shape)
  {
    this.shape = shape;
  }
  
  /**
   * Get the minimum size of this Component.
   * 
   * @return  The minimum size
   */
  public Dimension getMinimumSize()
  {
    return MINIMUM_SIZE;
  }
  
  /**
   * Get the preferred size of this Component.
   * 
   * @return  The preferred size
   */
  public Dimension getPreferredSize()
  {
    Rectangle bounds = shape.getBounds();
    return new Dimension((int)bounds.getWidth()+(int)bounds.getX(), 
                         (int)bounds.getHeight()+(int)bounds.getY());
  }
  
  /**
   * Render this Component.
   * 
   * @param g  The rendering engine to use
   */
  public void paint(Graphics g)
  {
    super.paint(g);
    
    if (shape != null)
    {
      // Remember state information
      Graphics2D g2 = (Graphics2D)g;
      AffineTransform at = g2.getTransform();
      Color color = g2.getColor();
      Paint paint = g2.getPaint();
      RenderingHints hints = g2.getRenderingHints();

      // Scale appropriately
      Rectangle componentBounds = getBounds();
      Rectangle shapeBounds     = shape.getBounds();
      double xScale = (double)componentBounds.getWidth() / 
          (double)(shapeBounds.getWidth() + shapeBounds.getX());
  
      double yScale = (double)componentBounds.getHeight() / 
          (double)(shapeBounds.getHeight() + shapeBounds.getY());
      
      double scale = Math.min(xScale,  yScale) - 0.10;

      
      // Render
      g2.scale(scale, scale);
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setPaint(PURPLE);
      g2.fill(shape);
      g2.setColor(GOLD);
      g2.draw(shape);
    
      // Reset the state information
      g2.setPaint(paint);
      g2.setColor(color);
      g2.setRenderingHints(hints);
      g2.setTransform(at);
    }
  }
 
}
        
Using a Custom Component (cont.)

The Renderer

javaexamples/cellrenderer/ShapeCellRenderer.java
import java.awt.*;
import javax.swing.*;

/**
 * A ListCellRenderer that can be used for a JList containing Shape objects.
 * 
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ShapeCellRenderer extends ShapeLabel implements ListCellRenderer<Shape> 
{
  private static final long serialVersionUID = 1L;

  /**
   * Default Constructor.
   */
  public ShapeCellRenderer()
  {
    super();
  }
  
  /**
   * Get the component that does the rendering.
   *
   * @param table      The JTable that is being rendered
   * @param value      The value of the cell to be rendered
   * @param isSelected true if the cell is to be rendered with highlighting
   * @param hasFocise  true if the cell has the focus
   * @param row        The row of the cell being rendered
   * @param column     The column of the cell being rendered
   * @return           The Component to use as the renderer
   */
  public Component getListCellRendererComponent(JList<? extends Shape> list, Shape value, int index,
      boolean isSelected, boolean cellHasFocus)
  {
    if (isSelected) setBorder(BorderFactory.createLineBorder(list.getForeground()));
    else            setBorder(BorderFactory.createLineBorder(list.getBackground()));
    setShape(value);

    return this;
  }

}
        
Using a Custom Component (cont.)

An Application

javaexamples/cellrenderer/ShapeCellRendererDemo.java
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

/**
 * A demonstration of a simple ListCellRenderer.
 * 
 * @author Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ShapeCellRendererDemo implements Runnable
{
  /**
   * Create the GUI.
   * 
   * @param args The command line arguments (which are ignored)
   * @throws Exception if something goes wrong
   */
  public static void main(String[] args) throws Exception
  {
    SwingUtilities.invokeAndWait(new ShapeCellRendererDemo());
  }
  
  /**
   * The code to execute in the event dispatch thread.
   */
  public void run()
  {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(640,  480);
    
    JPanel contentPane = (JPanel)f.getContentPane();

    Shape[] shapes = {new Rectangle2D.Double(10,10,50,50), new Ellipse2D.Double(10, 10, 50, 50),
        new Arc2D.Double(10, 10, 50, 50, 0., 270., Arc2D.PIE)};
    
    JList<Shape> list = new JList<Shape>(shapes);
    list.setCellRenderer(new ShapeCellRenderer());
    
    contentPane.add(list);
    f.setVisible(true);
  }
}