---
title: 'JComboxBox mit Separator nutzen'
url: 'https://tutego.de/blog/javainsel/2011/06/jcomboxbox-mit-separator-nutzen'
markdown: 'https://tutego.de/blog/javainsel/2011/06/jcomboxbox-mit-separator-nutzen.md'
date: '2011-06-20'
description: 'Standardmäßig unterstützt die JList und auch JComboBox keine Separatoren, doch die Unterteilung in Segmente ist in der Praxis sehr nützlich. Microsoft Word und PowerPoint benutzt sie zum Beispiel, um die zuletzt vom Benutzer ausgewählten Zeichensätze prominent oben in der Liste zu haben (Excel dage…'
taxonomy:
  category:
    - Insel
    - Swing
---

# JComboxBox mit Separator nutzen

![Illustration einer tropischen Insel mit Palmen und einem hohen Berg](https://tutego.de/blog/javainsel/user/themes/tutego/images/insel-cover.jpg)# Java Blog für Programmierer

[Java Blog für Programmierer](https://tutego.de/blog/javainsel/)

## JComboxBox mit Separator nutzen

20.06.2011 · [Christian Ullenboom](https://tutego.de/blog/javainsel/author/admin/)

Standardmäßig unterstützt die JList und auch JComboBox keine Separatoren, doch die Unterteilung in Segmente ist in der Praxis sehr nützlich. Microsoft Word und PowerPoint benutzt sie zum Beispiel, um die zuletzt vom Benutzer ausgewählten Zeichensätze prominent oben in der Liste zu haben (Excel dagegen nicht).

Wir wollen diese Möglichkeit nachbilden, und dabei noch einiges über Modelle und Renderer lernen.

Bei der Umsetzung gibt es unterschiedliche Varianten, die sich neben der technischen Implementierung darin unterscheiden, ob das Modell eine Markierung für den Separator enthält oder nicht. Wir stellen beide Ansätze vor und beginnen mit der ersten Variante, einem Zellen-Renderer Positionen mitzugeben, die sagen, wo ein Trennstrich zu zeichnen ist.

```java
JFrame frame = new JFrame();

frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

String[] items = { "Cambria", "Arial", "Verdana", "Times" };

JComboBox<String> comboBox = new JComboBox<String>( items );
```

**ListCellRenderer<`String`> renderer = new `SeparatorAwareListCellRenderer1`<`String`>(**

```java
comboBox.getRenderer(), 0 );

comboBox.setRenderer( renderer );

frame.add( comboBox );

frame.pack();

frame.setVisible( true );
```

Die eigene Klasse `SeparatorAwareListCellRenderer1` ist ein ListCellRenderer, den die JComboBox zur Darstellung der Komponenten nutzt. Im Konstruktor des Renderes geben wir den Original-Renderer mit – es kann ein bestimmter Renderer schon vorinstalliert sein, den wollen wir dekorieren – und ein variable Argumentliste von Positionen. Das Beispiel übergibt nur 0, da nach dem ersten Element (Index = 0) ein Trennzeichen zu setzen sein soll, sodass Cambria und Arial abgetrennt sind.

```java
package com.tutego.insel.ui.list;

import java.awt.*;

import java.util.Arrays;

import javax.swing.*;

public class SeparatorAwareListCellRenderer1<E> implements ListCellRenderer<E>

{

private final ListCellRenderer<? super E> delegate;

private final int[] indexes;

private final JPanel panel = new JPanel( new BorderLayout() );
```

public `SeparatorAwareListCellRenderer1`( ListCellRenderer<? super E> delegate, int… indexes )

```java
{

Arrays.sort( indexes );

this.delegate = delegate;

this.indexes = indexes;

}

@SuppressWarnings({ "rawtypes", "unchecked" })

@Override
```

public `Component` `getListCellRendererComponent`( JList list, E value,

int index, boolean `isSelected`,

boolean `cellHasFocus` )

```java
{

panel.removeAll();
```

**panel.add( delegate.`getListCellRendererComponent`( list, value, index,**

```java
isSelected, cellHasFocus ) );

if ( Arrays.binarySearch( indexes, index ) >= 0 )

panel.add( new JSeparator(), BorderLayout.PAGE_END );

return panel;

}

}
```

Die Implementierung basiert auf der Idee, jede Komponenten in einen Container (JPanel) zu setzen und in diesem Container dann je nach Bedarf ein JSeparator mit unten dran zu setzen. Statt dem JPanel mit einem JSeparator auszustatten kann ebenfalls auch ein Border unten gezeichnet werden. Die Anweisung `Arrays.binarySearch(indexes, index)` >= 0 ist als „contains“ zu verstehen, also ein Test, ob der Index im Feld ist – leider gibt es so eine Methode nicht in der Java-API. Wenn der Index im Feld ist, soll der Separator unter der Komponente erscheinen – dass sich eine Trennlinie auch am Anfang befinden kann berücksichtigt die Lösung nicht, und bleibt als Übungsaufgabe für die Leser.

Diese Lösung ist einfach, und funktioniert gut, denn vorhandene Renderer werden weiterverwendet, was sehr wichtig ist, denn größeren Swing-Anwendungen nutzen viele eigene Renderer, etwa um Icons und Text zusammenzufassen. Ein Nachteil ist, dass der Separator zu einen Element gehört, und wenn das Element etwa in der Liste ausgewählt wird, steht der Separator mit in der Selektion und ist nicht abgetrennt (das ist aber bei Word genauso).

Während die vorgestellte Variante in der Praxis gut funktioniert, wollen wir uns noch mit einer alternativen Umsetzung beschäftigen. Sie ist deutlicher komplexer und auch nicht so flexibel. Die Lösung basiert auf der Idee, dass die Modelldaten eine Markierung für den Separator enthalten – die folgende Lösung nutzt `null` dafür. Da JList oder JComboBox eine `null` problemlos verträgt, ist die Basis schnell umgesetzt:

```java
JFrame frame = new JFrame();

frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

String[] items = { "Cambria", null, "Arial", "Cambria", "Verdana", "Times" };

JComboBox<String> comboBox = new JComboBox<String>( items );

frame.add( comboBox );

frame.pack();

frame.setVisible( true );
```

Das Programm zum Start gebracht zeigt dort, wie das Model eine `null` liefert einfach nichts an. Die Selektion dieses `null`-Elements ist auch möglich und führt zu keiner Ausnahme.

Damit Swing das `null`-Element nicht als Leereintrag anzeigt, verpassen wir unserer JCombBox einen Zellen-Renderer. Der soll immer dann, wenn das Element `null` ist, ein Exemplar von JSeparator zeichnen. Vom Design ist es das beste, wenn der Zell-Renderer selbst die `null`-Elemente darstellt, aber das Zeichnen der nicht-`null`-Elemente an einen anderen Zell-Renderer abgibt, satt diese etwas durch einen BasicComboBoxRenderer (ein JLabel was ListCellRenderer implementiert) selbst zu renderen – das würde die Flexibilität der Lösung massiv einschränken.

```java
package com.tutego.insel.ui.list;

import java.awt.Component;

import javax.swing.*;

public class SeparatorAwareListCellRenderer2<E> implements ListCellRenderer<E>

{

private final ListCellRenderer<? super E> delegate;
```

public `SeparatorAwareListCellRenderer2`( ListCellRenderer<? super E> delegate )

```java
{

this.delegate = delegate;

}

@SuppressWarnings({ "rawtypes", "unchecked" })

@Override
```

public `Component` `getListCellRendererComponent`( JList list, E value,

int index, boolean `isSelected`,

boolean `cellHasFocus` )

```java
{

if ( value == null )

return new JSeparator();

return delegate.getListCellRendererComponent( list, value, index,

isSelected, cellHasFocus );

}

}
```

Die eigene Klasse `SeparatorAwareListCellRenderer2` bekommt einen anderen ListCellRenderer übergeben und liefert die Komponenten dieses Delegate-Renders immer dann, wenn das Element ungleich `null` ist. Ist es dagegen gleich `null`, liefert `getListCellRendererComponent()` ein Exemplar des JSeparators.

Im Beispielprogramm muss der Renderer nun angemeldet werden und dazu ist zu ergänzen:

`comboBox.setRenderer`(

```java
new SeparatorAwareListCellRenderer2<String>(comboBox.getRenderer()) );
```

Nach dem Start des Programms ist das Ergebnis schon viel Besser: dort wo vorher die JComboBox eine leere Zeile darstellte, ist nun ein Strich.

Die Lösung ist jedoch nur ein Etappensieg denn die Navigation mit der Tastatur durch die Liste zeigt eine Schwachstelle: Das `null`-Element lässt sich auswählen und erscheint auch als Linie im Editor/Textfeld. Beheben lässt sich das Problem nicht mit dem Renderer, denn der ist nur für die Darstellung in der Liste beauftragt. Hier muss in die Interna der Swing-Komponente eingegriffen werden. Jede Swing-Komponente hat ein korrespondierende UI-Delegate, das für das Verhalten und Darstellung der Komponente verantwortlich ist. Für die JComboBox sind das Unterklassen von ComboBoxUI und zwei Methoden sind besonders interessant: `selectNextPossibleValue()` und `selectPreviousPossibleValue()`.

Da jede UI-Implementierung ihre eines Look & Feel mitbringt, müssen wir hier eigentlichen einen Dekorator bauen und jede Methode bis auf die beiden genannten an das Original weiterleiten, doch das ist jetzt zu viel Arbeit und so nehmen wir die Basisklasse `WindowsComboBoxUI` als Basisklasse, denn unter Beispiel nutzt das Windows LaF. In der Unterklasse implementieren wir eigene Versionen der Methoden `selectNextPossibleValue()` und `selectPreviousPossibleValue()`, die so lange die Liste nach oben/unten laufen müssen, bis sie ein Element ungleich `null` finden.

Listing 1.34: com/tutego/insel/ui/list/SeparatorAwareComboBoxUI.java

```java
package com.tutego.insel.ui.list;

import com.sun.java.swing.plaf.windows.WindowsComboBoxUI;

public class SeparatorAwareComboBoxUI extends WindowsComboBoxUI

{

@Override
```

protected **void `selectNextPossibleValue()`**

```java
{

for ( int index = comboBox.getSelectedIndex() + 1;

index < comboBox.getItemCount();
```

index++ )

```java
if ( comboBox.getItemAt( index ) != null )

{

comboBox.setSelectedIndex( index );

break;

}

}

@Override
```

protected **void `selectPreviousPossibleValue()`**

```java
{

for ( int index = comboBox.getSelectedIndex() – 1;

index >= 0;
```

index– )

```java
if ( comboBox.getItemAt( index ) != null )

{

comboBox.setSelectedIndex( index );

break;

}

}

}
```

Das UI-Objekt muss ebenfalls angemeldet werden:

```java
comboBox.setUI( new SeparatorAwareComboBoxUI() );
```

Enthält die Liste `null`-Elemente überspringt die Tastennavigation über die Cursor-Taste diese. Doch auch mit diesem Teilstück fehlt ein weitere Detail: Mit feinem Klick lässt sich die Linie doch noch auswählen. Das ist keine Frage des Renderes und auch keine Frage der Tastaturnavigation – es muss untersagt werden, dass bei der Aktivierung ein `null`-Element in zum Editor kommen kann. Hier ist Methode `setSelectedItem()` von JComboBox entscheidend. Denn jedes Element was selektiert wird – und dadurch auch in der Textfeld kommt – geht durch die Methode durch. Wenn wir die Methode überschreiben, und bei `null`-Elemente einfach nichts tun, wird auch das `null`-Element nicht selektiert und im Textfeld bleibt das letzte Element.

Damit auch spezielle Implementierungen von JComboBox von diesem Verhalten profitieren könnten, müssen wir wieder einen Dekorator schreiben, doch das kostet zu viel Mühe, und so überschreibt eine einfache Unterklasse die `setSelectedItem()`-Methode. (Im Prinzip wäre auch eine überschiebene Methoden von `setSelectedIndex()` sinnvoll, denn das könnte eine programmierte Aktivierung der `null` vermeiden.)

```java
package com.tutego.insel.ui.list;

import javax.swing.*;

public class SeparatorAwareJComboBox<E> extends JComboBox<E>

{
```

public `SeparatorAwareJComboBox`( E… items )

```java
{

super( items );

}

@Override

public void setSelectedItem( Object anObject )

{

if ( anObject != null )

super.setSelectedItem( anObject );

}

}
```

Im Hauptprogramm muss nur diese spezielle Klasse zu Einsatz kommen:

```java
JComboBox<String> comboBox = new SeparatorAwareJComboBox<String>( items );
```

Kategorien: [Insel](https://tutego.de/blog/javainsel/category/insel/), [Swing](https://tutego.de/blog/javainsel/category/swing/)

## 1 Kommentare

**Thorsten** · [11.10.2012 15:01](#comment-4288)

Nutzt man das MS Windows SystemLookAndFeel, dann wird bei deinem Separator-Beispiel der weiße Panel-Hintergrund auch im nicht ausgeklappten Zustand gezeichnet. Dadurch fehlt der graue (bei MouseHover blaue) Farbverlauf. Durch ein `panel.setOpaque(false)` im Konstruktor wird's wieder richtig gemalt. Es gibt noch einige weitere Probleme in Verbindung mit der JGoodies Looks Bibliothek, die dort korrigiert werden, da an den ComboBox(-Renderern) nämlich schon etwas und erfordert noch einen weiteren Workaround: ```java ((JLabel) listCellRendererComponent).setForeground( super.getForeground() ); ((JLabel) listCellRendererComponent).setBackground( super.getBackground() ); ((JLabel) listCellRendererComponent).setBorder( super.getBorder() ); ((JLabel) listCellRendererComponent).setOpaque( isSelected ); ```

Die bisherigen Kommentare wurden aus WordPress übernommen. Neue Kommentare sind derzeit geschlossen.

[← Alle Beiträge](https://tutego.de/blog/javainsel/)

---

## Navigation

- Previous: [Open Symphony Projekt hat sich erledigt. Was sagt uns das?](https://tutego.de/blog/javainsel/2011/06/open-symphony-projekt-hat-sich-erledigt-was-sagt-uns-das.md)
- Next: [Google App Engine Update 1.5.1](https://tutego.de/blog/javainsel/2011/06/google-app-engine-update-1-5-1.md)
