Advanced Java Services | JList |
Bemerkung: Die add/removeListSelectionListener() Methoden werden auch von JList angeboten.
JScrollPane scrollPane = new JScrollPane(dataList); // Or in two steps: JScrollPane scrollPane = new JScrollPane(); scrollPane.getViewport().setView(dataList);
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 | |
Returntyp | Name 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. |
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.
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.
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.
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.
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.
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 !
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.
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) ;
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.
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.
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
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 | |
Konstante | Layout |
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.
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.
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()
{
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);
}
}
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.
Copyright Dipl.Math. H.M.Straub |