表格(排序)

表格(排序)

JTable的单元格和表头写完之后,关于JTable就剩下排序和过滤了,对于过滤来说,简单的直接使用SunRowSorter就可以了,复杂的可以集成Lucence等排序工具,对于它来说,性能比实现更重要,有时间就开个专题写,没时间就不写了,这里主要写排序.

排序从界面表现看其实就是对JTableHeader的鼠标操作,引发TableModel的数值比较,最后表现到画面的过程,知道这个过程很重要,它可以帮助我们完全实现自己的排序过程.

对于JavaJTable排序来说,主要分为两个阶段: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来处理的,这时我们看表格的数据行:

对于年份来说,这个排序是不对的,这个时候我们必须要复写TableModelgetColumnClass方法,告诉TableRowSorter我们现在需要排序的数据类型, TableRowSorter就会根据类型给我们的列设置合适的比较器Comparator

Collator.getInstance()

       @Override

        public Class<? extends Object> getColumnClass(int c) {

            return getValueAt(0, c).getClass();

        }

这是对JTable的排序就完成了:

但有时候还有别的情况,比如我们单元格放置的是一个特殊组件, TableRowSorter根本没有合适的比较器给我们提供,或者是基本类型但是我们不希望使用默认的比较器Comparator,这个时候我们就需要自己实现一个Comparator,然后通过TableRowSortersetComparator设置某一列一个新的比较器.比如我们希望我们的第二列按字符串的反序排序:

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的状态和监听,还要自己写HeaderRnederer,所以不是不能用1.6的话,最好不要自己实现.先看看是如何实现的,因为是Sun官方给的例子,就大概写下,算是学习和回忆以前使用1.5自己写Sorter的日子了.

效果和1.6类似:

首先看TableSorter,它继承AbstractTableModel,这样它也就相当于自己也是一种底层数据模型,可以记录数据和排序数据:

publicclass TableSorter extends AbstractTableModel {

然后看它的属性:

    private JTableHeader tableHeader;

这个是需要排序的JTableHeader,这里需要对它进行鼠标监听和状态管理.

    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) {

传入我们需要排序的JTableTableModel,并对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);

其它的还有一些设置比较器,设置JTableHeaderRenderer,绘制排序的图标的方法,基本都是以前JTable会学习到的,就不写了,很多但是不难.

然后是使用,先创建我们自己构造的TableSorter:

TableSorter sorter = new TableSorter(new MyTableModel());

当做JTableTableModel放入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();

然后设置JTableHeaderRenderer:

 table.getColumnModel().getColumn(i).setHeaderRenderer(renderer)

这样就完成了.



你可能感兴趣的:(表格(排序))