JTable sortieren

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen

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 folgendermaßen aus:

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;
    }
}
// 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 );
    }
}

JTableSort1.png

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:

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 );
    }

    [...]
}
// 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;
    }
}

Das Resultat: die Punkte werden zuerst nach der x, dann nach der y-Koordinate sortiert:

JTableSort2.png

3. Weitere Methoden

Der TableRowSorter bietet eine Reihe weiterer Methoden, die Aufmerksamkeit verdienen:

  • setStringConverter() setzt ein Delegate um aus Objects Strings zu generieren. Praktisch das Selbe, was die Methode toString() macht. Wenn ein TableStringConverter gesetzt ist, wird dieser Converter an Stelle 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, wie viele 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.