JTable的單元格和表頭寫完之后,關于JTable就剩下排序和過濾了,對于過濾來說,簡單的直接使用Sun的RowSorter就可以了,復雜的可以集成Lucence等排序工具,對于它來說,性能比實現更重要,有時間就開個專題寫,沒時間就不寫了,這里主要寫排序.
排序從界面表現看其實就是對JTableHeader的鼠標操作,引發TableModel的數值比較,最后表現到畫面的過程,知道這個過程很重要,它可以幫助我們完全實現自己的排序過程.
對于Java的JTable排序來說,主要分為兩個階段:Java1.6以前和Java1.6版本:Java1.6之前想要實現JTable的話,必須自己實現TableSorter,然后對JTableHeader去注冊,很麻煩,但是可以完全自己控制;Java1.6為了方便,Sun增加了排序和過濾的類:
javax.swing.table.
TableRowSorter<M extends TableModel> -à
javax.swing. DefaultRowSorter<M, I> -à
javax.swing. RowSorter<M>
我們可以直接使用了.
實現很容易,看Sun官方的例子:
RowSorter<TableModel> rowSorter = new TableRowSorter<TableModel>(table.getModel());
table.setRowSorter(rowSorter);
這是JTable就自動為你實現了過濾,它的好處是不污染任何的JTable數據和渲染,自然進行排序,結果如圖:
但是對于默認的JTable來說, TableRowSorter的默認排序都是把數據當做Object來處理的,這時我們看表格的數據行:
對于年份來說,這個排序是不對的,這個時候我們必須要復寫TableModel的getColumnClass方法,告訴TableRowSorter我們現在需要排序的數據類型, TableRowSorter就會根據類型給我們的列設置合適的比較器Comparator:
Collator.getInstance()
@Override
public Class<? extends Object> getColumnClass(int c) {
return
getValueAt(0, c).getClass();
}
這是對JTable的排序就完成了:
但有時候還有別的情況,比如我們單元格放置的是一個特殊組件, TableRowSorter根本沒有合適的比較器給我們提供,或者是基本類型但是我們不希望使用默認的比較器Comparator,這個時候我們就需要自己實現一個Comparator,然后通過TableRowSorter的setComparator設置某一列一個新的比較器.比如我們希望我們的第二列按字符串的反序排序:
TableRowSorter<TableModel> rowSorter = new TableRowSorter<TableModel>(table.getModel());
rowSorter.setComparator(1, new Comparator<String>() {
@Override
publicint compare(String str1, String str2) {
return
-str1.compareTo(str2);
}
});
table.setRowSorter(rowSorter);
效果如圖:
到此為止,Java1.6排序的基本使用就完成了,通過Sun的方法很簡單就實現了.
Java1.6以前的JTable排序功能必須自己實現,Sun官方也提供了例子,當然在1.6里你也可以自己實現,但是自己實現必須考慮排序Model,排序狀態,JTableHeader的狀態和監聽,還要自己寫Header的Rnederer,所以不是不能用1.6的話,最好不要自己實現.先看看是如何實現的,因為是Sun官方給的例子,就大概寫下,算是學習和回憶以前使用1.5自己寫Sorter的日子了.
效果和1.6類似:
首先看TableSorter類,它繼承AbstractTableModel,這樣它也就相當于自己也是一種底層數據模型,可以記錄數據和排序數據:
publicclass TableSorter extends
AbstractTableModel {
然后看它的屬性:
private
JTableHeader tableHeader;
這個是需要排序的JTable的Header,這里需要對它進行鼠標監聽和狀態管理.
private
MouseListener mouseListener;
private
TableModelListener tableModelListener;
這兩個是鼠標監聽器和TableModel監聽器,主要是當鼠標排序和JTabel數據變化時排序也隨之變化.
private
Map<Class<?>, Comparator<?>> columnComparators
= new
HashMap<Class<?>, Comparator<?>>();
private
List<Directive> sortingColumns
= new
ArrayList<Directive>();
這連個主要是記錄狀態的,一個是記錄列的排序比較器,一個是記錄列的排序狀態.
publicstaticfinal Comparator<Object> LEXICAL_COMPARATOR = new
Comparator<Object>() {
publicint compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
}
};
另外還提供了幾種基本的排序比較器,這是String的比較器.
然后是是構造函數,
public
TableSorter(TableModel tableModel) {
傳入我們需要排序的JTable的TableModel,并對JTable增加事件監聽和TableModel數據變化監聽:
this.tableHeader.addMouseListener(mouseListener);
this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(
this.tableHeader.getDefaultRenderer()));
this.tableModel.addTableModelListener(tableModelListener);
然后處理這些監聽:
privateclass MouseHandler extends
MouseAdapter {
@Override
publicvoid mouseClicked(MouseEvent e) {
首先取得排序列和當前排序狀態:
JTableHeader h = (JTableHeader)
e.getSource();
TableColumnModel columnModel =
h.getColumnModel();
int viewColumn
= h.columnAtPoint(e.getPoint());
int column =
columnModel.getColumn(viewColumn).getModelIndex();
int status = getSortingStatus(column);
然后判斷后調用排序方法:
setSortingStatus(column, status);
TableModel數據變化監聽也類似:
privateclass TableModelHandler implements
TableModelListener {
@Override
publicvoid tableChanged(TableModelEvent e) {
非排序狀態直接插入:
if (!isSorting()) {
clearSortingState();
fireTableChanged(e);
return;
}
排序狀態比較插入:
int column = e.getColumn();
if (e.getFirstRow() == e.getLastRow()
&& column != TableModelEvent.ALL_COLUMNS
&& getSortingStatus(column) == NOT_SORTED
&& modelToView != null) {
int viewIndex =
getModelToView()[e.getFirstRow()];
fireTableChanged(new TableModelEvent(TableSorter.this,
viewIndex, viewIndex, column,
e.getType()));
return;
}
然后是一個行的比較器:
// Helper
classes
privateclass Row implements Comparable<Object> {
它取得比較的行:
@Override
publicint compareTo(Object o) {
int row1 = modelIndex;
int row2 =
((Row) o).modelIndex;
再去的比較的值:
Object o1 = tableModel.getValueAt(row1,
column);
Object o2 = tableModel.getValueAt(row2,
column);
根據我們提供的構造器比較:
comparison =
getComparator(column).compare(o1, o2);
其它的還有一些設置比較器,設置JTableHeader的Renderer,繪制排序的圖標的方法,基本都是以前JTable會學習到的,就不寫了,很多但是不難.
然后是使用,先創建我們自己構造的TableSorter:
TableSorter sorter = new
TableSorter(new MyTableModel());
當做JTable的TableModel放入JTable中:
JTable table = new
JTable(sorter);
還要設置一下Header,目的是使用我們自己做成的HeaderRenderer,可以出現圖標
sorter.setTableHeader(table.getTableHeader());
以后就和正常JTable一樣了.
最后,我們看一個我們自己實現排序比較和Renderer的例子:

工程目錄如下:
其中和類是實現了Icon接口的繪制JTableHeader排序時的圖片的類,
publicclass MyBlankIcon implements Icon {
publicclass MySortIcon implements Icon {
主要過程就是實現paintIcon方法繪制圖片,前面寫過這種三角形形式的圖片繪制方法了,這里省略.
void paintIcon(Component c, Graphics g, int x, int y);
然后就是比較重要的MyTableSorter類,它繼承TableRowSorter,擴充了我們自己的排序實現:
publicclass MyTableSorter extends
TableRowSorter<TableModel> {
提供了三個方法,正序、逆序和無序:
/**
* Enumeration value indicating the items
are sorted in increasing
*/
protectedvoid setASCSort(int colIdx) {
ArrayList<SortKey> key = new ArrayList<SortKey>();
key.add(new
RowSorter.SortKey(colIdx, SortOrder.ASCENDING));
setSortKeys(key);
}
/**
* Enumeration value indicating the items
are sorted in decreasing * order.
*/
protectedvoid setDESCSort(int colIdx) {
ArrayList<SortKey> key = new ArrayList<SortKey>();
key.add(new RowSorter.SortKey(colIdx,
SortOrder.DESCENDING));
setSortKeys(key);
}
/**
* Enumeration value indicating the items
are unordered.
*/
protectedvoid setUNSort(int colIdx) {
ArrayList<SortKey> key = new ArrayList<SortKey>();
key.add(new
RowSorter.SortKey(colIdx, SortOrder.UNSORTED));
setSortKeys(key);
}
提供設置當前排序狀態的方法:
protectedvoid setSorterStatus(int column, boolean CtrlDown) {
List<SortKey>
SortStatus = new ArrayList<SortKey>(getSortKeys());
SortKey sortKey = null;
int sortIndex =
-1;
for (sortIndex
= SortStatus.size() - 1; sortIndex >= 0; sortIndex--) {
if
(SortStatus.get(sortIndex).getColumn() == column) {
break;
}
}
if (sortIndex == -1) {
// Key doesn't exist
if (CtrlDown)
{
sortKey = new SortKey(column, SortOrder.DESCENDING);
} else {
sortKey = new SortKey(column, SortOrder.ASCENDING);
}
SortStatus.add(0, sortKey);
} elseif (sortIndex == 0) {
// It's the primary sorting key, toggle it
SortStatus.set(0,
toggle(SortStatus.get(0), CtrlDown));
} else {
// It's not the first, but was sorted on, remove old
// entry, insert as first with ascending.
SortStatus.remove(sortIndex);
if (CtrlDown)
{
sortKey = new SortKey(column, SortOrder.DESCENDING);
} else {
sortKey = new SortKey(column, SortOrder.ASCENDING);
}
SortStatus.add(0, sortKey);
}
if
(SortStatus.size() > getMaxSortKeys()) {
SortStatus = SortStatus.subList(0,
getMaxSortKeys());
}
setSortable(column, true);
setSortKeys(SortStatus);
setSortable(column, false);
}
提供取得下一個狀態的方法:
protected SortKey
toggle(SortKey key, boolean CtrlDown) {
if
(key.getSortOrder() == SortOrder.ASCENDING) {
returnnew SortKey(key.getColumn(), SortOrder.DESCENDING);
} elseif (key.getSortOrder() == SortOrder.DESCENDING) {
returnnew SortKey(key.getColumn(), SortOrder.UNSORTED);
} else {
returnnew SortKey(key.getColumn(), SortOrder.ASCENDING);
}
}
然后就是Renderer方法了,它繼承TableCellRenderer,設置排序狀態和Header狀態:
publicclass MyHeaderButtonRenderer extends JButton implements
TableCellRenderer, MouseListener {
它提供一個屬性存儲當前列的排序狀態:
/** save
the header state. */
private
Hashtable<Integer, Integer> state = null;
在它的鼠標事件里,設置排序和排序狀態:
/**
* Invoked when the mouse button has been
clicked (pressed and
* released) on a component.
*/
@Override
publicvoid mouseClicked(MouseEvent e) {
先取得鼠標事件的列,設置表現:
int col = header.columnAtPoint(e.getPoint());
int sortCol = header.getTable().convertColumnIndexToModel(col);
renderer.setPressedColumn(col);
renderer.setSelectedColumn(col);
header.repaint();
再設置排序和狀態:
if (header.getTable().isEditing()) {
header.getTable().getCellEditor().stopCellEditing();
}
if (!(DOWN == renderer.getState(col))
&& !(UP == renderer.getState(col))) {
sorter.setUNSort(sortCol);
}
實現TableCellRenderer的方法設置列的Header的表現:
@Override
public Component
getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (header != null && renderer != null) {
boolean isPressed =
(column == pushedColumn);
button.getModel().setPressed(isPressed);
button.getModel().setArmed(isPressed);
}
使用也不是很麻煩:
先創建Sorter:
TableSorter tableSorter = new MyTableSorter(table);
設置某一列的比較器,和TableSorter一樣:
tableSorter.setComparator(2, new MyComparator<String>());
table.setRowSorter(tableSorter);
然后是創建Renderer:
MyHeaderButtonRenderer renderer = new MyHeaderButtonRenderer();
然后設置JTable的Header的Renderer:
table.getColumnModel().getColumn(i).setHeaderRenderer(renderer);
這樣就完成了.