Advanced   Java   Services JList Back Next Up Home


JList und seine Umgebung

JListEnv1.jpg

Bemerkung: Die add/removeListSelectionListener() Methoden werden auch von JList angeboten.


Grundlegende Eigenschaften
Konstruktoren
JList() Creates a JList with an empty model. Der Konstruktor ist nur sinnvoll, wenn nnachträglich ein ListModel gesetzt wird.
JList(ListModel dataModel)Creates a JList that displays the elements in the specified, non-null model.
JList(Object[] listData)
 
 
 
 
 
 
 
 
 
 
 
 
 
Creates a JList that displays the elements in the specified array.

public JList(Object[] listData)
{
   this( new AbstractListModel()
         {
            public int getSize() { return listData.length; }
            public Object getElementAt(int i) { return listData[i]; }
         }
       );
}
Der Quellcode des Konstruktors zeigt, daß hier ein ListModel "on the fly" als anonyme Klasse erzeugt wird. Es gibt keine Methoden, mit denen man neue Elemente in die Liste aufnhmen kann. Die Liste ist fest und nicht veränderbar. Man kann allerdings mit den setListData() Methoden neue Werte darstellen. Die alte Liste wird dabei verworfen.
JList(Vector<?> listData)Creates a JList that displays the elements in the specified Vector. In diese Liste können beliebig viele Einträge erfolgen.
Einige Methoden
ReturntypName der Methode
void
 
addListSelectionListener(ListSelectionListener listener)
Adds a listener to the list that's notified each time a change to the selection occurs.
void
 
removeListSelectionListener(ListSelectionListener listener)
Removes a listener from the list that's notified each time a change to the selection occurs.
void
 
ensureIndexIsVisible(int index)
Scrolls the viewport to make the specified cell completely visible.
Rectangle
 
getCellBounds(int index0, int index1)
Returns the bounds of the specified range of items in JList coordinates.
ListCellRenderer
 
getCellRenderer()
Returns the object that renders the list items.
void
 
setCellRenderer(ListCellRenderer cellRenderer)
Sets the delegate that's used to paint each cell in the list.
boolean
 
getDragEnabled()
Gets the dragEnabled property.
void
 
setDragEnabled(boolean b)
Sets the dragEnabled property, which must be true to enable automatic drag handling (the first part of drag and drop) on this component.
int
 
getFirstVisibleIndex()
Returns the index of the first visible cell.
int
 
getFixedCellHeight()
Returns the fixed cell height value -- the value specified by setting the fixedCellHeight property, rather than that calculated from the list elements.
void
 
setFixedCellHeight(int height)
Sets the height of every cell in the list.
int
 
getFixedCellWidth()
Returns the fixed cell width value -- the value specified by setting the fixedCellWidth property, rather than that calculated from the list elements.
void
 
setFixedCellWidth(int width)
Sets the width of every cell in the list.
int
 
getLastVisibleIndex()
Returns the index of the last visible cell.
int
 
getLayoutOrientation()
Returns JList.VERTICAL if the layout is a single column of cells, or JList.VERTICAL_WRAP if the layout is "newspaper style" with the content flowing vertically then horizontally or JList.HORIZONTAL_WRAP if the layout is "newspaper style" with the content flowing horizontally then vertically.
void
 
setLayoutOrientation(int layoutOrientation)
Defines the way list cells are layed out.
ListModel
 
getModel()
Returns the data model that holds the list of items displayed by the JList component.
void
 
setModel(ListModel model)
Sets the model that represents the contents or "value" of the list and clears the list selection after notifying PropertyChangeListeners.
Dimension
 
getPreferredScrollableViewportSize()
Computes the size of the viewport needed to display visibleRowCount rows.
void
 
setListData(Object[] listData)
Constructs a ListModel from an array of objects and then applies setModel to it.
void
 
setListData(Vector<?> listData)
Constructs a ListModel from a Vector and then applies setModel to it.
Object
 
getPrototypeCellValue()
Returns the cell width of the "prototypical cell" -- a cell used for the calculation of cell widths, because it has the same value as all other list items.
void
 
setPrototypeCellValue(Object prototypeCellValue)
Computes the fixedCellWidth and fixedCellHeight properties by configuring the cellRenderer to index equals zero for the specified value and then computing the renderer component's preferred size.
int
 
getSelectedIndex()
Returns the first selected index; returns -1 if there is no selected item.
void
 
setSelectedIndex(int index)
Selects a single cell.
int[]
 
getSelectedIndices()
Returns an array of all of the selected indices in increasing order.
void
 
setSelectedIndices(int[] indices)
Selects a set of cells.
Object
 
getSelectedValue()
Returns the first selected value, or null if the selection is empty.
void
 
setSelectedValue(Object anObject, boolean shouldScroll)
Selects the specified object from the list.
Object[]
 
getSelectedValues()
Returns an array of the values for the selected cells.
Color
 
getSelectionBackground()
Returns the background color for selected cells.
void
 
setSelectionBackground(Color selectionBackground)
Sets the background color for selected cells.
Color
 
getSelectionForeground()
Returns the selection foreground color.
void
 
setSelectionForeground(Color selectionForeground)
Sets the foreground color for selected cells.
void
 
getIcon()
Returns
void
 
setSelectionInterval(int anchor, int lead)
Selects the specified interval.
int
 
getSelectionMode()
Returns whether single-item or multiple-item selections are allowed.
void
 
setSelectionMode(int selectionMode)
Determines whether single-item or multiple-item selections are allowed.
ListSelectionModel
 
getSelectionModel()
Returns the value of the current selection model.
void
 
setSelectionModel(ListSelectionModel selectionModel)
Sets the selectionModel for the list to a non-null ListSelectionModel implementation.
int
 
getVisibleRowCount()
Returns the preferred number of visible rows.
void
 
setVisibleRowCount(int visibleRowCount)
Sets the preferred number of rows in the list that can be displayed without a scrollbar, as determined by the nearest JViewport ancestor, if any.
Point
 
indexToLocation(int index)
Returns the origin of the specified item in JList coordinates.
int
 
locationToIndex(Point location)
Convert a point in JList coordinates to the closest index of the cell at that location.
boolean
 
isSelectedIndex(int index)
Returns true if the specified index is selected.


Listen mit Strings

Das folgende Applet bringt eine einfache List mit Stringobjekten. Die Einträge sind die Bandmitglieder der Duke Ellington BigBand auf dem Newport Jazz Festival 1956, in dem Duke Ellington ein historisches Comeback erlebte. Über die Radiobuttons kann man entscheiden, ob die Liste Mehrfachmarkierungen erlaubt oder nicht. Wenn Sie einen einzelnen Musiker auswählen, wird in der Statuszeile angezeigt, welches Instrument er gespielt hat. Wenn Sie einen Bereich wählen können und den richtigen Bereich markieren, dann erscheint eine entsprechende Meldung in der Statuszeile.


Ausschnitte aus dem Code
private String[] musicians =
   { " Cat Anderson", " Willie Cook", " Ray Nance", " Clark Terry",
     " Quentin Jackson", " John Sanders", " Britt Woodman",
     " Johnny Hodges", " Russel Procope", " Paul Gonsalves", " Jimmy Hamilton", " Harry Carney",
     " Duke Ellington", " Jimmy Woode", " Sam Woodyard" };
//...
list = new JList(musicians) ;
list.setVisibleRowCount(6) ; // Anzahl der sichtbaren Einträge (default ist 8)
list.setFixedCellHeight(27); // Sets the height of every cell in the list.
list.setFixedCellWidth(150); // Sets the width of every cell in the list.
list.addListSelectionListener( new TheListSelectionListener() ) ;
scrollpane = new JScrollPane(list);
//...

Setzt man die Größe einer Zelle nicht selbst (wie oben), so ermittelt die Methode getCellBounds() die Größe einer Zelle, in dem sie unter Zuhilfenahme des Renderers die Größe einer Zelle abschätzt. Mit setFixedCellHeight(27) und setFixedCellWidth(150) erzwingen wir hier eine großzügigere Darstellung.


Listen mit Bildern (Icons)

Für eine Liste von Bildern benötigt man (wie oben) keine speziellen ListCellRenderer. Das Beispiel verwendet also den DefaultListCellRenderer und ein automatisch erzeugtes ListModel.


Ausschnitte aus dem Code
try
{
   ImageIcon dukes[] = { new ImageIcon( new URL( getCodeBase(), "duke1.gif")),
                         new ImageIcon( new URL( getCodeBase(), "duke2.gif")),
                         new ImageIcon( new URL( getCodeBase(), "duke3.gif")),
                         new ImageIcon( new URL( getCodeBase(), "duke4.gif")),
                         new ImageIcon( new URL( getCodeBase(), "duke5.gif")),
                         new ImageIcon( new URL( getCodeBase(), "dukebutler.gif"))
                        };

   list = new JList(dukes);
   list.setVisibleRowCount(3) ;  // default ist 8
   list.setSelectedIndex(0) ;  // default: keine Zelle ausgewählt
   list.setFixedCellHeight(75);
   list.setFixedCellWidth(150);

   list.setBackground( new Color(230,240,250) );
   sp = new JScrollPane(list);
   centerPanel.add(sp) ;
   conPane.add(centerPanel, BorderLayout.CENTER) ;
}
catch(MalformedURLException e)
{ }

Im Unterschied zum ersten Beispiel setzen wir hier mit list.setSelectedIndex(0) explizit eine Zelle als ausgewählte Zelle. Wie im ersten Beispiel kann man auch hier mit dem DefaultListCellRenderer arbeiten, den mit der Initialisierung eines JList-objektes geschenkt bekommt.


Listen mit Text und Bildern (Icons)

Eine Liste mit Icons (es könnten auch Bilder sein) und Text, die zusammen jeweils einen Eintrag in der Liste bilden. Zu den vorgegeben Farben können weitere zufällige Farben erzeugt werden. Außerdem können Einträge gelöscht werden.

Hier wird ein Icon und einen Text gemeinsam dargestellt. Das schafft der DefaultListCellRenderer nicht. Wir müssen uns einen passenden Renderer selbst schreiben. Zunächst jedoch wollen wir eine Klasse entwerfem, die ein einfarbiges Rechteck darstellt. Sie wird das Interface Icon implementieren. Sie kapselt eine Farbe, die Größe des Rechtecks und eine Beschreibung der Farbe. Falls die Farbe keine Standardfarbe ist, so enthält die Beschreibung die RGB-Werte der Farbe.


Die Klasse MonoChromeIcon
import java.awt.*;
import javax.swing.*;


public class MonoChromeIcon implements Icon
{
   private int w, h;
   private Color col ;
   private String description;

   public MonoChromeIcon(Color col, int w, int h)
   {
      this.w = w;
      this.h = h;
      this.col = col;
   }
   public MonoChromeIcon(Color col, String desc, int w, int h)
   {
      description = desc;
      this.w = w;
      this.h = h;
      this.col = col;
   }

   public String getDescription()
   {
      return description;
   }

   // --------------- vom Interface Icon geforderte Methoden --------------- \\
   public void paintIcon(Component c, Graphics g, int x, int y)
   {
      //System.out.println(c);
      Color oldColor = g.getColor();

      g.setColor(Color.black);
      g.drawRect(x, y, w-1, h-1);

      g.setColor(col);
      g.fillRect(x+1, y+1, w-2, h-2);

      g.setColor(oldColor);
   }

   public int getIconWidth()
   {
      return w;
   }
   public int getIconHeight()
   {
      return h;
   }
}

Nun schreiben wir einen Renderer, der mit dieser Klasse zusammenarbeiten kann. Der DefaultListCellRenderer ist ja ein JLabel, das zusätzlich die Methode getListCellRendererComponent() aus dem Interface ListCellRenderer implementiert. Letztere Methode gibt die darzustellende Komponente zurück, also this im Falle des DefaultListCellRenderer. Dieser Returnwert dient als eine Art Stempel, der die Einträge in die JList stempelt. Aus diesem Grund kann eine JList nicht editierbar werden. Der hier verwendete Renderer ist eine kleine Abwandlung des DefaultListCellRenderer, da ja ein JLabel ohne weiteres Text und Bild darstellen kann. Der zweite Parameter der Methode getListCellRendererComponent() enthält das darzustellende Objekt !


Der Renderer IconTextListCellRenderer
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

public class IconTextListCellRenderer extends DefaultListCellRenderer
{

   public IconTextListCellRenderer()
   {
      setHorizontalTextPosition(RIGHT);
      setHorizontalAlignment(LEFT);
   }

   public Component getListCellRendererComponent( JList list, Object value, int index,
                boolean isSelected, boolean cellHasFocus)
   {
      if(value!=null)
      {
         Icon icon= (Icon)value ;
         String text;

         if(value instanceof MonoChromeIcon)
         {
            text =((MonoChromeIcon)icon).getDescription();
         }
         else if(value instanceof ImageIcon)
         {
            text =((ImageIcon)icon).getDescription();
         }
         else
         {
            throw new IllegalArgumentException("second parameter must be instanceof MonoChromeIcon or instanceof ImageIcon");
         }
         setIcon(icon);
         setText(text);

         if (isSelected)
         {
            setEnabled(true);
            setBackground(Color.white);
            setForeground(Color.blue);
         }
         else
         {
            setBackground( new Color(200,210,220) );
            setForeground(Color.black);
            setBorder(null);
         }
         // Border sollte hier gesetzt werden
         if (cellHasFocus)
         {
            setBorder( new LineBorder(Color.blue));
         }
         else
         {
            setBorder(new EmptyBorder(1, 1, 1, 1) );
         }
      }
      return this ;
   }

}

Wir haben den Renderer so flexibel ausgelegt, daß statt eine MonoChromiconobjektes auch ein Imageicon übergeben werden kann. Auch in diesem Fall wird dann der von getDescription() gelieferte Text dargestellt.


Als letztes noch ein Ausschnitt aus der Konfiguration der Liste
Color[] cols = { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray,
                 Color.green, Color.lightGray, Color.magenta, Color.orange,
                 Color.pink, Color.red, Color.white, Color.yellow };

String[] names = { "black", "blue", "cyan", "darkGray", "gray", "green", "lightGray",
                   "magenta", "orange", "pink", "red", "white", "yellow" };

defaultListModel = new DefaultListModel();
for(int i=0; i<cols.length; i++)
{
   defaultListModel.addElement( new SingleColorIcon(cols[i], names[i], 29, 29)  );
}
list = new JList(defaultListModel);
IconTextListCellRenderer itlcr = new IconTextListCellRenderer() ;
list.setCellRenderer(itlcr);
list.setFixedCellWidth(175) ;
list.setFixedCellHeight(31) ;

Listen mit anderen Objekten

Eine Liste mit JCheckBoxen, bzw. mit den Abbildern von JCheckBoxen. Sobald man ein Item markiert hat kann man mit der rechten Maustaste den Haken setzen oder löschen. In der Statuszeile wird angezeigt, welche Einträge gescheckt sind. Das Beispiel arbeitet mit einem eigenen ListCellRenderer.

Auch hier brauchen wir kein eigenes Model, sondern nur einen eigenen Renderer. Da das DefaultListModel einen Vector kaspelt, der bekanntlich beliebige Objekte aunfnehmen kann, braucht man selten ein eigenes Model, aber relativ schnell einen eigenen Renderer. Hier verwenden wir nicht einmal das DefaultListModel, sondern legen ein Array von Checkboxen an, die wir der JList per Konstruktor übergeben. Der Renderer leitet sich diemal nicht von DefaultListCellRenderer ab, sondern implementiert zur Abwechslung nur das Interface ListCellRenderer.


Der Renderer CheckBoxListCellRenderer
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

public class CheckBoxListCellRenderer implements ListCellRenderer
{
   private Color backGr, foreGr, selBgr = new Color(250,230,240) ; //violetta
   private Color selFgr = new Color(0, 0, 170);

   public Component getListCellRendererComponent( JList list, Object value, int index,
                boolean isSelected, boolean cellHasFocus)
   {
      if(value!=null && value instanceof JCheckBox )
      {
         JCheckBox cb = (JCheckBox)value;
         cb.setBorderPainted(true);

         if (isSelected)
         {
            //cb.setEnabled(true);
            cb.setBackground(selBgr);
            cb.setForeground(selFgr);
         }
         else
         {
            cb.setBackground(backGr);
            cb.setForeground(foreGr);
            cb.setBorder(null);
         }
         // Border sollte hier gesetzt werden
         if (cellHasFocus)
         {
            //setBorder(UIManager.getBorder("List.focusCellHighlightBorder") );
            cb.setBorder( new LineBorder(selFgr));
         }
         else
         {
            cb.setBorder(new EmptyBorder(1, 1, 1, 1) );
         }
         return cb ;
      }
      else
      {
         throw new IllegalArgumentException
               ("second parameter is not an instance of JCheckBox"); // Exception werfen
      }
   }
}

Und wie schafft man es, daß die man den Abdruck einer JCheckBox an- und aushaken kann ? Dazu vewenden wir einen MouseListener für die Liste und die Methode locationToIndex(), die einem zu einer Mausposition in einer Liste den zugehörigen Index liefert.


Ausschnitt aus dem Code
class ListClickHandler extends MouseAdapter
{
   public void mouseClicked(MouseEvent e)
   {
      int index = list.locationToIndex(e.getPoint());
      if( list.isSelectedIndex(index) )
      {
         if(e.getButton() == MouseEvent.BUTTON3)
         {
            boxes[index].setSelected( !boxes[index].isSelected() ) ;
            list.updateUI();
         }
      }
   }  // end mouseClicked
}  // end class

Waagrechtes Scrollen

Bis jetzt waren alle Listen ausschließlich vertikal scrollbar. Seit der Version 1.4 ist auch ein waagrechtes Scrollen einer Liste möglich. Mit dem Methodenpaar get/setLayoutOrientation kann man ein waagrechtes Scrollen initiieren. Die Methoden bekommen bzw. liefern drei in 1.4 eingeführte Konstanten. Die folgende Tabelle zeigt ihre Wirkungsweise.

Waagrechtes und senkrechtes Scrollen
KonstanteLayout
JList.VERTICAL item 1
item 2
item 3
item 4
JList.HORIZONTAL_WRAP item 1    item 2
item 3    item 4
JList.VERTICAL_WRAP item 1    item 3
item 2    item 4

JList.VERTICAL ist default. Meistens wird man waagrechte Liste mit JList.VERTICAL_WRAP anlegen, bei der nur eine Spalte sichtbar ist. Bevor eine waagrecht scrollbare Liste vorgestellt wird, noch eine generelle Information über das Scrollen.


public Dimension getPreferredScrollableViewportSize()

Durch Aufruf der Methode getPreferredSize() erfährt ein LayoutManager, die Größe einer Komponente, die er darstellen soll. Ähnliches gilt für die Methode getPreferredScrollableViewportSize(). Durch Sie erfährt ein JViewport, welchen Ausschnitt es von der Komponente darstellen soll. Demgemäß liefert getPreferredScrollableViewportSize() ein Teilrechteck des Rechtecks, das getPreferredSize() liefert. Kurz bevor ein JViewport (z.Bsp. in einem JScrollPane) sichtbar wird, ruft es die Methode getPreferredScrollableViewportSize() und setzt seine eigene Größe auf die von der Methode erhaltene Größe. Für eine vertikale Liste arbeitet getPreferredScrollableViewportSize() nach folgendem Algorithmus: Falls die Größe einer Zelle über setFixedCellWidth() und setFixedCellHeight() gesetzt wurde, wird zusammen mit getVisibleRowCount() die Größe des Rechteckausschnittes ermittelt. Falls nicht, wird mit die Methode getCellBounds() gearbeitet. Diese ersetzt dann getFixedCellWidth() und getFixedCellHeight(). Seltsamerweise wird für eine waagrechte Liste dieser Algorithmus außer Kraft gesetzt und getPreferredScrollableViewportSize() gibt dann einfach den Wert von getPreferredSize() zurück, was zur Folge hat, daß die Liste zwar mehrspaltig angeordnet ist, aber ohne einen waagrechten Scrollbalken. Aus dem Quellcode der Methode kann man dieses Verhalten ablesen.


Quellcode von public Dimension getPreferredScrollableViewportSize()
public Dimension getPreferredScrollableViewportSize()
{
   if (getLayoutOrientation() != VERTICAL)
   {
      return getPreferredSize();
   }
   Insets insets = getInsets();
   int dx = insets.left + insets.right;
   int dy = insets.top + insets.bottom;

   int visibleRowCount = getVisibleRowCount();
   int fixedCellWidth = getFixedCellWidth();
   int fixedCellHeight = getFixedCellHeight();

   if ((fixedCellWidth > 0) && (fixedCellHeight > 0))
   {
      int width = fixedCellWidth + dx;
      int height = (visibleRowCount * fixedCellHeight) + dy;
      return new Dimension(width, height);
   }
   else if (getModel().getSize() > 0)
   {
      int width = getPreferredSize().width; // nicht geeignet für waagrechtes Scrollen
      int height;
      Rectangle r = getCellBounds(0, 0);
      if (r != null)
      {
         height = (visibleRowCount * r.height) + dy;
      }
      else
      {
         // Will only happen if UI null, shouldn't matter what we return
         height = 1;
      }
      return new Dimension(width, height);
   }
   else
   {
      fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth : 256;
      fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight : 16;
      return new Dimension(fixedCellWidth, fixedCellHeight * visibleRowCount);
   }
}

Damit die Methode auch für waagrechtes Scrollen gute Ergebnisse liefert, braucht es nur eine kleine Änderung. Hier die verbesserte Methode:


public Dimension getPreferredScrollableViewportSize() (auch) für waagrechtes Scrollen
public Dimension getPreferredScrollableViewportSize()
{
   Insets insets = getInsets();
   int dx = insets.left + insets.right;
   int dy = insets.top + insets.bottom;

   int visibleRowCount = getVisibleRowCount();
   int fixedCellWidth = getFixedCellWidth();
   int fixedCellHeight = getFixedCellHeight();

   if ((fixedCellWidth > 0) && (fixedCellHeight > 0))
   {
      int width = fixedCellWidth + dx;
      int height = (visibleRowCount * fixedCellHeight) + dy;
      return new Dimension(width, height);
   }
   else if (getModel().getSize() > 0)
   {
      int width = getPreferredSize().width; // nicht geeignet für waagrechtes Scrollen
      int height;
      Rectangle r = getCellBounds(0, 0);
      if (r != null)
      {
         height = (visibleRowCount * r.height) + dy;
         //////////////////////////////////////////////////
          if (getLayoutOrientation() != VERTICAL)
          {
             width = r.width + dx ;
          }
         //////////////////////////////////////////////////
      }
      else
      {
         // Will only happen if UI null, shouldn't matter what we return
         height = 1;
      }
      return new Dimension(width, height);
   }
   else
   {
      fixedCellWidth = (fixedCellWidth > 0) ? fixedCellWidth : 256;
      fixedCellHeight = (fixedCellHeight > 0) ? fixedCellHeight : 16;
      return new Dimension(fixedCellWidth, fixedCellHeight * visibleRowCount);
   }
}

Wie setzt man die korrigierte Methode ein

Braucht man nur ein JListobjekt und will für dieses waagrechtes Scrollen, so überschreibt man die Methode getPreferredScrollableViewportSize() wie oben in einer anonymen Klasse. Braucht man dagegen mehrere JListobjekt, so ist es effektiver, JList abzuleiten und in der Ableitung die Methode getPreferredScrollableViewportSize() zu überschreiben.

Valid XHTML 1.0 Strict Copyright Dipl.Math. H.M.Straub top Back Next Up Home