Siehe Klassen-Kommentar. Wenn jemand weiß wie die richtige UI-Resourcen heißen, wenn es sie denn gibt, wäre es nett wenn derjenige das abändern könnte. Ich hab nirgendwo ne Liste gefunden wie die ganzen Dinger heißen, woher weiss man sowas?!
Verbesserungsvorschläge natürlich erwünscht, ansonsten müsste denke ich alles passen
Verbesserungsvorschläge natürlich erwünscht, ansonsten müsste denke ich alles passen
Java:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.UIManager;
import javax.swing.RowSorter.SortKey;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
/**
* Diese Demo zeigt einen JTable, der immer nach allen Spalten sortiert ist.
* Dabei wird die linkeste Spalte in der View am höchsten priorisiert, die
* rechteste am niedrigsten.
*
* ********************************* ACHTUNG: **********************************
*
* Die Icons für den TableHeader (siehe Klasse CustomTableHeaderRenderer)
* funktionieren nicht, da die Resourcen erfunden sind. Falls der UI Manager
* keine solchen Pfeile anbietet, benutzt man halt eigene Bilder, oder man
* zeichnet die Pfeile direkt ins Panel hinein.
*
* Und: Bei dieser Demo wird nicht auf das Editieren des Tables eingegangen.
* Wenn man möchte, dass sich die Sortierung anpasst nach ßndern eines
* Zellinhaltes, oder nach dem Hinzufügen/Löschen von Spalten, muss dies
* nachträglich implementiert werden. Im Endeffekt muss man aber immer nur die
* List von SortKeys aktualisieren.
*
* *****************************************************************************
*
* @author thomas klutsch Sep 13, 2009
*
*/
public class MultipleSortTableDemo extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new MultipleSortTableDemo().setVisible(true);
}
});
}
private JTable table;
public MultipleSortTableDemo() {
super("MultipleSortTableDemo");
setDefaultCloseOperation(EXIT_ON_CLOSE);
/* Table erstellen */
table = new JTable(getRowData(), getColumnNames());
/* Wir erstellen den default-mässigen RowSorter */
table.setAutoCreateRowSorter(true);
/* Anfangs sind alle Spalten aufsteigend sortiert */
List<SortKey> sortKeys = new ArrayList<SortKey>();
for (int i = 0; i < table.getColumnCount(); i++) {
sortKeys.add(new SortKey(i, SortOrder.ASCENDING));
}
table.getRowSorter().setSortKeys(sortKeys);
/*
* Wir installieren einen Listener, der Verschiebungen der Spalten
* mitbekommt und die Sortierung entsprechend anpasst
*/
table.getColumnModel().addColumnModelListener(
new ResortingColumnModelListener());
/*
* Der default-mäßige Mouse-Listener des ColumnHeaders erstellt immer
* eine komplett eigene Liste von SortKeys, und zerstört damit unsere.
* Deshalb können wir ihn nicht beibehalten. Allerdings wollen wir Dinge
* wie z.B. das Verschieben der Spalten nicht selber implementieren.
* Also klauen wir uns den Listener, und ersetzen ihn durch einen
* teilweise eigenen".
*/
JTableHeader header = table.getTableHeader();
for (MouseListener ml : header.getMouseListeners()) {
if (ml instanceof BasicTableHeaderUI.MouseInputHandler) {
/* Den hier wollen wir haben */
MouseListener altered = new AlteredTableHeaderListener(
(BasicTableHeaderUI.MouseInputHandler) ml);
/* Jetzt können wir ihn runterschmeißen */
header.removeMouseListener(ml);
/* und unseren eigenen adden */
header.addMouseListener(altered);
}
}
/*
* Nachdem wir nun das Verhalten beim Klick auf eine Spalte geändert
* haben, ist der default-mäßige HeaderRenderer auch nicht mehr sehr
* nützlich, da er uns immer nur die Sortier-Richtung von einer Spalte
* anzeigt. Also machen wir uns einen eigenen
*/
header.setDefaultRenderer(new CustomTableHeaderRenderer());
add(new JScrollPane(table));
pack();
}
private Object[][] getRowData() {
Object[] row1 = { "Andreas", "Schmidt", 37, 12 };
Object[] row2 = { "Andreas", "Huber", 21, 92 };
Object[] row3 = { "Chris", "Huber", 33, 12 };
Object[] row4 = { "David", "Zykolli", 19, 24 };
Object[] row5 = { "Michaela", "Lang", 21, 45 };
Object[] row6 = { "Michaela", "Bauer", 21, 46 };
return new Object[][] { row1, row2, row3, row4, row5, row6 };
}
private Object[] getColumnNames() {
return new Object[] { "Vorname", "Nachname", "Alter", "Glückszahl" };
}
class ResortingColumnModelListener implements TableColumnModelListener {
@Override
public void columnMoved(TableColumnModelEvent e) {
int from = e.getFromIndex();
int to = e.getToIndex();
if (from != to) {
int fromModel = table.convertColumnIndexToModel(from);
int toModel = table.convertColumnIndexToModel(to);
// Wir vertauschen die SortKeys der Spalten from und to, und
// übernehmen alle anderen aus der alten Sortierung
RowSorter<?> sorter = table.getRowSorter();
List<? extends SortKey> oldKeys = sorter.getSortKeys();
List<SortKey> newKeys = new ArrayList<SortKey>();
for (SortKey k : oldKeys) {
if (fromModel == k.getColumn()) {
// from -> to
newKeys.add(getSortKeyWithColumn(toModel, oldKeys));
} else if (toModel == k.getColumn()) {
// to -> from
newKeys.add(getSortKeyWithColumn(fromModel, oldKeys));
} else {
// einfach kopieren
newKeys.add(k);
}
}
// update
sorter.setSortKeys(newKeys);
}
}
private SortKey getSortKeyWithColumn(int col,
List<? extends SortKey> oldKeys) {
for (SortKey k : oldKeys) {
if (k.getColumn() == col) {
return k;
}
}
// Das sollte nie passieren...
return null;
}
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnMarginChanged(ChangeEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
}
class AlteredTableHeaderListener implements MouseListener,
MouseMotionListener {
/*
* Der übergebene Listener ist sowohl MouseListener als auch
* MouseMotionListener. Wir brauchen beide.
*/
private MouseListener original;
private MouseMotionListener originalMotion;
public AlteredTableHeaderListener(
BasicTableHeaderUI.MouseInputHandler original) {
this.original = original;
this.originalMotion = original;
}
/**
* Diese Methode implementieren wir nun selber:
*/
@Override
public void mouseClicked(MouseEvent e) {
// zuerst einmal wollen wir die Spalte wissen, auf die geklickt
// wurde
int colView = table.columnAtPoint(e.getPoint());
int colModel = table.convertColumnIndexToModel(colView);
/*
* Nun erstellen wir unsere neue Sortierung: Die Reihenfolge bleibt
* die gleiche, aber wir kehren die SortOrder der angeklickten
* Spalte um.
*
* INFO: SortKeys haben keine Setter, deswegen müssen wir komplett
* neue erstellen.
*/
RowSorter<?> sorter = table.getRowSorter();
List<? extends SortKey> oldKeys = sorter.getSortKeys();
List<SortKey> newKeys = new ArrayList<SortKey>();
for (SortKey k : oldKeys) {
if (k.getColumn() == colModel) {
// SortOrder vertauschen
SortOrder oldOrder = k.getSortOrder();
SortOrder newOrder = (oldOrder == SortOrder.ASCENDING ? SortOrder.DESCENDING
: SortOrder.ASCENDING);
newKeys.add(new SortKey(colModel, newOrder));
} else {
// einfach übernehmen
newKeys.add(k);
}
}
// update
sorter.setSortKeys(newKeys);
}
/*
* --- Alle weiteren können wir vom Original übernehmen. ---
*/
@Override
public void mouseEntered(MouseEvent e) {
original.mouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
original.mouseExited(e);
}
@Override
public void mousePressed(MouseEvent e) {
original.mousePressed(e);
}
@Override
public void mouseReleased(MouseEvent e) {
original.mouseReleased(e);
}
@Override
public void mouseDragged(MouseEvent e) {
originalMotion.mouseDragged(e);
}
@Override
public void mouseMoved(MouseEvent e) {
originalMotion.mouseMoved(e);
}
}
class CustomTableHeaderRenderer extends JPanel implements TableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
/* Titel der Spalte */
title.setText(value.toString());
/* Wir holen uns die jetzige SortOrder für diese Spalte */
List<? extends SortKey> sortKeys = table.getRowSorter()
.getSortKeys();
SortOrder o = sortKeys.get(column).getSortOrder();
order.setIcon(o == SortOrder.ASCENDING ? ascIcon : descIcon);
/*
* Wir zeigen jetzt noch die seitlichen Pfeile an, um den User
* darauf aufmerksam zu machen, dass man Spalten verschieben kann.
*/
arrowLeft.setIcon(column > 0 ? leftIcon : null);
arrowRight.setIcon(column < table.getColumnCount() - 1 ? rightIcon
: null);
return this;
}
private JLabel title;
private JLabel order;
private JLabel arrowLeft, arrowRight;
private Icon ascIcon, descIcon;
private Icon leftIcon, rightIcon;
public CustomTableHeaderRenderer() {
title = new JLabel();
order = new JLabel();
arrowLeft = new JLabel();
arrowRight = new JLabel();
ascIcon = UIManager.getIcon("arrow_up");
descIcon = UIManager.getIcon("arrow_down");
leftIcon = UIManager.getIcon("arrowLeft");
rightIcon = UIManager.getIcon("arrowRight");
/*
* Links und rechts sind die Pfeile, die die Verschiebung andeuten.
* In der Mitte befindet sich der Titel mit dem Pfeil der
* Sortier-Richtung.
*/
setLayout(new BorderLayout());
JPanel titleAndOrder = new JPanel();
titleAndOrder.setLayout(new FlowLayout());
titleAndOrder.add(title);
titleAndOrder.add(order);
add(titleAndOrder, BorderLayout.CENTER);
add(arrowLeft, BorderLayout.WEST);
add(arrowRight, BorderLayout.EAST);
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
}
}
}