Die Aufgabe besteht wohl auch für Sie aus mehreren Teilen:
Die Lösung hierfür ist einfach. Die Daten eine Tabelle werden üblicherweise in einer
Collection
vorgehalten. Die Klasse java.util.Collections
stellt hierfür die Methode sort
bereit. Unsere Aufgabe ist es lediglich
das Interface java.util.Comparable
zu implementieren.
Gehen wir jedoch Schritt für Schritt vor.
package de.bastie.sample; import java.util.ArrayList; import javax.swing.table.AbstractTableModel; /** * Dies ist das einfache Beispiel Tabellenmodell. * @author Bastie - Sebastian Ritter * @version zuletzt getestet mit Java 1.5 */ public class SimpleTabellenModel extends AbstractTableModel { /** Unsere Fachobjekte, die wir in der Tabelle darstellen wollen. */ protected ArrayList<Person> fachObjekte = new ArrayList<Person> (); public SimpleTabellenModel () { this.loadData (); } /** * Hier sorgen wir dafür, dass wir auch Fachobjekte zur Verfügung haben. */ protected void loadData () { fachObjekte.add (new Person ("Ritter", "Sebastian", "05.09.1975", "verheiratet")); fachObjekte.add (new Person ("Ritter", "Sebastián", "unbekannt", "ledig")); fachObjekte.add (new Person ("Knight", "Sebastian", "01.01.2222", "ledig")); fachObjekte.add (new Person ("Knight", "Sebastián", "05.09.1975", "ledig")); } private String [] kopfNamen = new String [] {"Nachname","Vorname","Geburtsdatum","Familienstand"}; public int getAnzahlDerKopfNamen () { return this.kopfNamen.length; } public String getColumnName (final int spalte) { if (spalte < this.getAnzahlDerKopfNamen()) { return kopfNamen [spalte]; } else { return super.getColumnName(spalte); } } public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } public int getRowCount() { return this.fachObjekte.size(); } public int getColumnCount() { return 4; } public Object getValueAt(final int zeile, final int spalte) { switch (spalte) { case 0 : return this.fachObjekte.get(zeile).getName (); case 1 : return this.fachObjekte.get(zeile).getVorname(); case 2 : return this.fachObjekte.get(zeile).getFormatiertesGeburtsdatum(); case 3 : return this.fachObjekte.get(zeile).getFamilienstand(); default: return null; } } }
Dazu gehören noch unsere Fachklassen - hier die Person:
package de.bastie.sample; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.Logger; /** * Beispiel BO * @author Bastie - Sebastian Ritter */ public class Person { private SimpleDateFormat sdf = new SimpleDateFormat ("dd.MM.yyyy"); public Person (){} public Person (final String name, final String vorname,final String gebDatumFormat_tt_mm_jjjj,final String familienstand) { this.setName(name); this.setVorname(vorname); this.setGeburtsdatum(gebDatumFormat_tt_mm_jjjj); this.setFamilienstand(familienstand); } public String getFamilienstand() { return familienstand; } public void setFamilienstand(String familienstand) { this.familienstand = familienstand; } public Date getGeburtsdatum () { return this.geburtsdatum; } public String getFormatiertesGeburtsdatum() { return sdf.format(this.geburtsdatum); } public void setGeburtsdatum (final String tt_mm_jjjj) { try { this.geburtsdatum = sdf.parse(tt_mm_jjjj); } catch (final ParseException e) { Logger.global.throwing(this.getClass().getName() ,"setGeburtsdatum", e); this.geburtsdatum = new Date (0l); } } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVorname() { return vorname; } public void setVorname(String vorname) { this.vorname = vorname; } private String name; private String vorname; private String familienstand; private Date geburtsdatum = new Date (0l); }
Wie bereits erwähnt müssen wir unser Model sortierbar machen. Zu diesem Zweck führen
wir die Methode sort
ein. Da wir nach den Eigenschaften der Person sortieren
wollen brauchen wie dafür noch einen PersonComparator
hier als innere Klasse
realisiert.
package de.bastie.sample; import java.util.Collections; import java.util.Comparator; /** * Ein Sortierbares Tabellenmodell für Personen * @author Bastie - Sebastian Ritter * @version zuletzt getestet mit Java 1.5 */ public class SortierbaresTabellenModel extends SimpleTabellenModel { public void sort (final int spalte) { Collections.sort(this.fachObjekte, new PersonComparator (spalte)); } private class PersonComparator implements Comparator { private final int spalte; public PersonComparator (final int spalte) { this.spalte = spalte; } public int compare(Object o1, Object o2) { if (o1 == null && o2 == null) { return 0; } else if (o1 == null) { return 1; } else if (o1 instanceof Person && o2 instanceof Person) { switch (this.spalte) { case 0 : return ((Person)o1).getName().compareTo(((Person)o2).getName()); case 1 : return ((Person)o1).getVorname().compareTo(((Person)o2).getVorname()); case 2 : return ((Person)o1).getGeburtsdatum().compareTo(((Person)o2).getGeburtsdatum()); case 3 : return ((Person)o1).getFamilienstand().compareTo(((Person)o2).getFamilienstand()); default : return 0; } } else { return 1; } } } }
Das war es auch schon - wir können unsere Tabelle sortieren. Aufgabe 1 erledigt.
Ein Aufruf der Funktion könnte zum Beispiel über einen ActionListener
wie diesen erfolgen.
package de.bastie.sample; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JTable; /** * Sortieren einer JTable mit dem sortierbaren Tabellenmodell. * @author Bastie - Sebastian Ritter * @version zuletzt getestet mit Java 1.5 */ public class SortTableActionListener implements ActionListener { private final JTable tabelle; public SortTableActionListener(final JTable tabelle) { this.tabelle = tabelle; } public void actionPerformed(ActionEvent e) { if (tabelle.getModel() instanceof SortierbaresTabellenModel) { ((SortierbaresTabellenModel)tabelle.getModel()).sort(Integer.parseInt(e.getActionCommand())); } } }
...oder anders ausgedrückt - wie bekomme ich die Sortierung per Klick auf den
Tabellenkopf? Das ganze ist etwas schwerer. Leider können wir der JTable nicht einfach
einen Listener hierfür anhängen. Im folgenden Stelle ich vier Klassen vor, welche
das Problem "universell" lösen. Hierbei handelt es sich um eine Erweiterung von
Sun Klassen, um die Möglichkeit einen ActionListener
an den Tabellenkopf
zu hängen. Wenn Sie diese Klassen verwenden besteht Ihre Aufgabe lediglich in dem
registrieren eines ActionListeners und Implementieren eines Comparators für Ihre
Fachobjekte. Die vier vorgestellten Klassen verhalten sich ansonsten wie eine
normale Swing JTable.
Wir erweitern die JTable um unseren Tabellenkopf und unser SpaltenModell zu registrieren.
package de.bastie.swing.tabelle; import java.util.logging.Logger; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; /** * Erweiterung der JTable um nützlich Funktionen. * @author Bastie - Sebastian Ritter * @version last tested with Java 1.5 */ public class BstJTable extends JTable { public BstJTable () { this (null, null); } public BstJTable (final TableModel model) { this (model, null); } /** * Do not use this Constructor because the TableColumnModel * is replacing. * @param model model that represents the business * @param columnModel is ignored * @param selectionModel how can users selected * @deprecated TableColumnModel is ignored. Better you use one of the simplifier constructors. */ public BstJTable (final TableModel model, final TableColumnModel columnModel, final ListSelectionModel selectionModel) { this (model, selectionModel); Logger.getLogger(this.getClass().getName()).warning("TableColumnModel ignored - used constructor ist deprecated"); } /** * Construct a new JTable with a selectable table header. *
All other constructors calls him. * @param model model that represents the business * @param selectionModel how can users selected */ public BstJTable (final TableModel model, final ListSelectionModel selectionModel) { super (model, null, selectionModel); // Own ColumnModel this.setColumnModel(new BstTableColumnModel()); // Own TableHeader this.setTableHeader(new BstJTableHeader()); this.getTableHeader().setColumnModel(this.getColumnModel()); } public BstJTableHeader getTableHeader () { return (BstJTableHeader) this.tableHeader; } }
In unserem Model für unsere Tabellenspalten legen wir lediglich unseren
Darsteller (Renderer
) fest. Dies wäre zwar grds. nicht notwendig aber
ein bisschen schön soll es ja auch aussehen.
package de.bastie.swing.tabelle; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.TableColumn; /** * TableColumnModel to react from selection a table header. * @author Bastie - Sebastian Ritter */ public class BstTableColumnModel extends DefaultTableColumnModel { public void addColumn (final TableColumn aColumn) { super.addColumn(aColumn); this.addMyHeaderRenderer (aColumn); } /** * @param column */ private void addMyHeaderRenderer(final TableColumn column) { column.setHeaderRenderer(new BstSelectionTableCellRenderer ()); } }
Der Darsteller selbst erzeugt lediglich für jeden Spaltennamen ein
JLable
Objekt und formatiert diese je nachdem, ob es gerader
mit der Maus angeklickt wurde.
package de.bastie.swing.tabelle; import java.awt.Component; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.table.TableCellRenderer; /** * TableCellRenderer for select a column header. * @author Bastie - Sebastian Ritter * @version last tested with Java 1.5 */ public class BstSelectionTableCellRenderer implements TableCellRenderer { private int pressedColumn = -1; public Component getTableCellRendererComponent (final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { JLabel b = new JLabel ((value == null) ? "" : value.toString()); // Dann stell das ganze mal richtig dar... if (column == this.pressedColumn) { b.setBackground(UIManager.getColor("control")); b.setBorder(BorderFactory.createEtchedBorder(UIManager.getColor("controlHighlight"),UIManager.getColor("controlShadow"))); } else { b.setBackground(UIManager.getColor("controlShadow")); b.setBorder(BorderFactory.createEtchedBorder(UIManager.getColor("controlLtHighlight"),UIManager.getColor("controlDkShadow"))); } return b; } public void setPressedColumn (final int col) { this.pressedColumn = col; } }
Wenden wir uns nun der Klasse BstJTableHeader
zu. Diese macht im
wesentlich folgendes:
ActionListener
n bereit.Renderer
) über die Klicks (drücken und loslassen)package de.bastie.swing.tabelle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; /** * TableHeader for action on selection. * @author Bastie - Sebastian Ritter * @version last tested with Java 1.5 */ public class BstJTableHeader extends JTableHeader implements MouseListener { public BstJTableHeader () { this (null); } public BstJTableHeader (final TableColumnModel columnModel) { super (columnModel); this.addMouseListener(this); } private ArrayListlistener = new ArrayList (); public void addActionListener (final ActionListener listener) { this.listener.add(listener); } public void removeActionListener (final ActionListener listener) { this.listener.remove(listener); } /** * Fire a ActionEvent with the table model column as * command. * @param column * @param when * @param modifiers */ protected void fireActionEvent (final int column, final long when, final int modifiers) { final ActionEvent e = new ActionEvent (this, ActionEvent.ACTION_PERFORMED, ""+this.getTable() .getColumnModel() .getColumn(column) .getModelIndex(), when, modifiers); this.fireActionEvent(e); } protected void fireActionEvent (final ActionEvent e) { for (int i = 0; i < listener.size(); i++) { listener.get(i).actionPerformed(e); } } /** * Method calls fireActionEvent with this table column of view. * @param e MouseEvent */ public void mouseClicked (final MouseEvent e) { this.fireActionEvent(this.columnAtPoint(e.getPoint()),e.getWhen(),e.getModifiers()); } public void mousePressed(MouseEvent e) { JTableHeader header = (JTableHeader) e.getSource(); int column = header.columnAtPoint(e.getPoint()); TableCellRenderer cr = header.getTable() .getColumnModel() .getColumn(column) .getHeaderRenderer(); if (cr instanceof BstSelectionTableCellRenderer) { ((BstSelectionTableCellRenderer) cr).setPressedColumn(column); header.repaint(); } } public void mouseReleased(MouseEvent e) { JTableHeader header = (JTableHeader) e.getSource(); int column = header.columnAtPoint(e.getPoint()); TableCellRenderer cr = header.getTable().getColumnModel().getColumn(column).getHeaderRenderer(); if (cr instanceof BstSelectionTableCellRenderer) { ((BstSelectionTableCellRenderer) cr).setPressedColumn(-1); header.repaint(); } } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} }
Für den Test ist natürlich noch eine kleine Anwendung notwendig. Das Ergebnis sehen Sie am Ende...
package de.bastie.sample; import java.awt.BorderLayout; import javax.swing.JApplet; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; import de.bastie.swing.tabelle.BstJTable; /** * Dieses Beispiel verdeutlicht, wie du auf einen Mausklick * auf den Tabellenkopf reagieren kannst. * @author Bastie - Sebastian Ritter * @version zuletzt getestet mit Java 1.5 */ public class BeispielKlickbarerTabellenKopf extends JApplet { private JTable tabelle; /** * Initialisierung unserer Anwendung / unseres Applets */ public void init () { // Wir initialisieren unsere Ansicht ... BstJTable tabelle = new BstJTable (); tabelle.getTableHeader().addActionListener(new SortTableActionListener (tabelle)); tabelle.setModel (new SortierbaresTabellenModel()); this.getContentPane().add (new JScrollPane (tabelle)); } /** * Die Startmethode initialisiert die Anwendung für das Stand-Alone-Beispiel. * @param args */ public static void main (final String [] args) { JFrame fenster = new JFrame ("Beispiel klickbarer Tabbellenkopf"); fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); fenster.setSize(400,300); BeispielKlickbarerTabellenKopf beispiel = new BeispielKlickbarerTabellenKopf (); beispiel.init(); fenster.getContentPane().add(beispiel, BorderLayout.CENTER); fenster.setVisible(true); beispiel.start(); } }Java Archiv mit Quellen und Klassen. all rights reserved © Bastie - Sebastian Ritter @: w³: http://www.Bastie.de