JTable sortieren
Mit Java 1.6 wurde Swing weiterentwickelt. Unter anderem kann die JTable nun sortiert werden.
Für die Sortierung muss man beim JTable einen RowSorter<? extends TableModel> über die Methode setRowSorter registrieren. Die JTable wird diesen RowSorter benutzen um die Einträge des TableModels hin und her zu schieben.
1. Der TableRowSorter
Zwar wäre es möglich einen RowSorter selbst zu implementieren, die weitaus einfachere Variante ist aber die Benutzung der Klasse TableRowSorter. Der unkonfigurierte TableRowSorter sortiert jeweils eine Spalte. Sollten die Elemente dieser Spalte eine natürliche Ordnung (durch das Interface Comparable definiert) haben, so wird diese Ordnung benutzt. Andernfalls werden die Elemente in Strings umgewandelt, und mittels eines Collators verglichen.
Das sieht dann folgendermassen aus: <code=java> import java.awt.*; import java.util.Random;
import javax.swing.*; import javax.swing.table.*;
public class JTableDemo{
public static void main( String[] args ){ // Ein TableModel mit zufälligen Werten füllen TableModel model = randomModel(); // Die JTable initialisieren JTable table = new JTable( model ); table.setDefaultRenderer( Point.class, new PointRender()); // Der TableRowSorter wird die Daten des Models sortieren TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(); // Der Sorter muss dem JTable bekannt sein table.setRowSorter( sorter ); // ... und der Sorter muss wissen, welche Daten er sortieren muss sorter.setModel( model );
JFrame frame = new JFrame( "Demo" ); frame.getContentPane().add( new JScrollPane( table ) ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); frame.pack(); frame.setVisible( true ); } // Diese Methode generiert ein TableModel mit zufälligen Werten private static TableModel randomModel(){ Object[] names = { "String", "Integer", "Point" }; DefaultTableModel model = new DefaultTableModel( names, 0 ){ @Override public Class<?> getColumnClass( int column ) { switch( column ){ case 0: return String.class; case 1: return Integer.class; case 2: return Point.class; default: return Object.class; } } };
Random random = new Random(); for( int i = 0; i < 100; i++ ){ Object[] row = { randomString( random ), randomInt( random ), new Point( randomInt( random ), randomInt( random )) }; model.addRow( row ); } return model; } // Generiert einen zufälligen String private static String randomString( Random random ){ int length = random.nextInt( 4 ) + 4; StringBuilder result = new StringBuilder( length ); for( int i = 0; i < length; i++ ) result.append( (char)('a' + random.nextInt( 'z' - 'a' + 1 ))); return result.toString(); } // Generiert einen zufälligen int private static int randomInt( Random random ){ return random.nextInt( 1000 ) + 1; }
} </code=java>
<code=java> // Ein Renderer für java.awt.Point public class PointRender extends DefaultTableCellRenderer{
@Override public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) { Point point = (Point)value; String text = point.x + " / " + point.y; return super.getTableCellRendererComponent( table, text, isSelected, hasFocus, row, column ); }
}</code=java>
2. Ein eigener Comparator
Im Beispiel des 1. Abschnittes wurden die String- und die Integer-Spalte sinnvoll sortiert, die Point-Spalte sah hingegen komisch aus. Damit auch diese Spalte sinnvoll sortiert wird (ohne dass man eine neue Point-Klasse implementiert) muss man einen Comparator bereitstellen. Dieser Comparator kann man über die Methode setComparator des TableRowSorters einbinden.
Der Code vom Beispiel oben leicht abgeändert: <code=java> import java.awt.*; import java.util.Comparator; import java.util.Random;
import javax.swing.*; import javax.swing.table.*;
public class JTableDemo{
public static void main( String[] args ){ // Ein TableModel mit zufälligen Werten füllen TableModel model = randomModel(); // Die JTable initialisieren JTable table = new JTable( model ); table.setDefaultRenderer( Point.class, new PointRender()); // Der TableRowSorter wird die Daten des Models sortieren TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(); // Der Sorter muss dem JTable bekannt sein table.setRowSorter( sorter ); // ... und der Sorter muss wissen, welche Daten er sortieren muss sorter.setModel( model );
// Den Comparator für die 2. Spalte (mit den Points) setzen. sorter.setComparator( 2, new PointComparator()); JFrame frame = new JFrame( "Demo" ); frame.getContentPane().add( new JScrollPane( table ) ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); frame.pack(); frame.setVisible( true ); }
[...]
}</code=java>
<code=java> // Ein Comparator für Punkte public class PointComparator implements Comparator<Point>{
public int compare( Point o1, Point o2 ) { if( o1.x < o2.x ) return -1; else if( o1.x > o2.x ) return 1; else if( o1.y < o2.y ) return -1; else if( o1.y > o2.y ) return 1; else return 0; }
}</code=java> Das Resultat: die Punkte werden zuerst nach der x, dann nach der y-Koordinate sortiert:
=3. Weitere Methoden
Der TableRowSorter bietet eine Reihe weitere Methoden die Aufmerksamkeit verdienen:
- setStringConverter setzt ein Delegate um aus Objects Strings zu generieren. Praktisch dasselbe was die Methode toString macht. Wenn ein TableStringConverter gesetzt ist, wird dieser Converter anstelle der toString-Methode benutzt, um einen sortierbaren String aus unbekannten Objekten zu generieren. Vorteilhaft ist dieses vorgehen, wenn die toString-Methode nicht überschrieben werden kann (weil die Objekte z.B. aus einer Library kommen), oder verschiedene Tabellen nach unterschiedlichen Kriterien sortieren sollen.
- setMaxSortKeys sagt dem Sorter, wieviele Spalten beim Sortieren beachtet werden sollen. Angenommen der Wert ist auf 3, der Benutzer sortiert zuerst Spalte 2, dann 1: sollte nun in Spalte 1 ein Element doppelt vorkommen, so werden die Zeilen nach der 2. Spalte sortiert. Würde der Benutzer noch auf die 4. Spalte klicken, so würde zuerst nach der 4., dann nach der 1., dann nach der 2. Spalte sortiert.
- setRowFilter setzt einen RowFilter. Der RowFilter entscheidet in seiner include-Methode zeilenweise, welche Zeilen angezeigt werden sollten. Ein RowFilter eignet sich z.B. für eine Suchfunktion: sobald der Benutzer in ein Textfeld etwas eintippt, erlaubt der RowFilter nur noch diejenigen Daten, welche zu dem eingetippten Text passen. Der Sorter kann über die Methode sort aufgefordert werden, den veränderten Filter zu benutzen.