如何用空行对java JTable进行排序并强制空行总是最后?

时间:2010-02-23 08:03:45

标签: java swing sorting jtable

我正在使用JTable,在表格的底部有一个空行,以便能够在表格中添加新行。

在空行中插入或写入数据后,我自动添加一个新的空行。 (它假设像Microsoft视觉表一样)

我正在使用java默认行排序器

问题是我需要空行一直是最后一行!但在对表格进行排序后,它将成为第一行。

DefaultRowSorter 类的 compare(int model1,int model2)“方法获得2个行号,如果第一行的值为1,则返回-1如果第二行的值为null,则返回null和1。并且为了得到颠倒的顺序,将它逐渐减去-1。

            //Treat nulls as < then non-null
            if (v1 == null) {
                if (v2 == null) {
                    result = 0;
                } else {
                    result = -1;
                }
            } else if (v2 == null) {
                result = 1;
            } else {
                result = sortComparators[counter].compare(v1, v2);
            }
            if (sortOrder == SortOrder.DESCENDING) {
                result *= -1;
            }

空行被排序为最小值,并且DESCENDING将是第一行(因为mult乘以-1)并导致很多问题。

我可以覆盖它,并且在DESCENDING模式下,空行(通常是最后一行)不会多次-1,它将是任何排序后的最后一行。 但问题是“比较”方法及其调用者“”内部类在 DefaultRowSorter 中是私有的。

有没有办法避免排空空行并使其始终排在最后一行?

5 个答案:

答案 0 :(得分:5)

我尝试了一下,我想我找到了解决方案。

我没有在TableModel中创建那个空行,而是在JTable中伪造它,只在用户实际输入一些数据时创建它。

RowSorter仅对TableModel的行进行排序,因此我们的行不受影响,仍然是最后一行。

public class NewLineTable extends JTable {

    @Override
    public int getRowCount() {
        // fake an additional row
        return super.getRowCount() + 1;
    }

    @Override
    public Object getValueAt(int row, int column) {
        if(row < super.getRowCount()) {
            return super.getValueAt(row, column);
        }
        return ""; // value to display in new line
    }

    @Override
    public int convertRowIndexToModel(int viewRowIndex) {
        if(viewRowIndex < super.getRowCount()) {
            return super.convertRowIndexToModel(viewRowIndex);
        }
        return super.getRowCount(); // can't convert our faked row
    }

    @Override
    public void setValueAt(Object aValue, int row, int column) {
        if(row < super.getRowCount()) {
            super.setValueAt(aValue, row, column);
        }
        else {
            Object[] rowData = new Object[getColumnCount()];
            Arrays.fill(rowData, "");
            rowData[convertColumnIndexToModel(column)] = aValue;
            // That's where we insert the new row.
            // Change this to work with your model.
            ((DefaultTableModel)getModel()).addRow(rowData);
        }
    }
}

答案 1 :(得分:2)

DefaultRowSorter肯定存在缺陷。

唯一的选择是根据DefaultRowsorter创建自己的RowSorter并更正问题。

答案 2 :(得分:1)

我通过仅仅 TableRowSorter 进行子类化找到了另一种解决方案。

我们知道的DefaultRowSorter文档:

  

比较器永远不会传递null

当继承 DefaultRowSorter.ModelWrapper 时,我们可以返回一个特殊的Null-Object并创建一个自定义比较器来处理该值。

以下是我的代码。可能它不像自定义RowSorter实现那么高效,它可能仍然包含一些错误,我没有测试所有内容,但是根据我的要求它可以工作。

class EmptyTableRowSorter<M extends AbstracTableModel> extends TableRowSorter<M> {

    private static final EmptyValue emptyValue = new EmptyValue();

    public EmptyTableRowSorter(M model) {
        super(model);
    }

    @Override
    public void modelStructureChanged() {
        // deletes comparators, so we must set again
        super.modelStructureChanged();

        M model = getModelWrapper().getModel();
        for (int i = 0; i < model.getColumnCount(); i++) {
            Comparator<?> comparator = this.getComparator(i);
            if (comparator != null) {
                Comparator wrapper = new EmptyValueComparator(comparator, this, i);
                this.setComparator(i, wrapper);
            }
        }
    }

    @Override
    public void setModel(M model) {
        // also calls setModelWrapper method
        super.setModel(model);

        ModelWrapper<M, Integer> modelWrapper = getModelWrapper();
        EmptyTableModelWrapper emptyTableModelWrapper = new EmptyTableModelWrapper(modelWrapper);

        // calls modelStructureChanged method
        setModelWrapper(emptyTableModelWrapper);
    }

    /**
     * The DefaulRowSorter implementation does not pass null values from the table
     * to the comparator.
     * This implementation is a wrapper around the default ModelWrapper,
     * returning a non null object for our empty row that our comparator can handle.
     */
    private class EmptyTableModelWrapper extends DefaultRowSorter.ModelWrapper {

        private final DefaultRowSorter.ModelWrapper modelWrapperImplementation;

        public EmptyTableModelWrapper(ModelWrapper modelWrapperImplementation) {
            this.modelWrapperImplementation = modelWrapperImplementation;
        }

        @Override
        public Object getModel() {
            return modelWrapperImplementation.getModel();
        }

        @Override
        public int getColumnCount() {
            return modelWrapperImplementation.getColumnCount();
        }

        @Override
        public int getRowCount() {
            return modelWrapperImplementation.getRowCount();
        }

        @Override
        public Object getValueAt(int row, int column) {
            M model = EmptyTableRowSorter.this.getModel();

            // my model has the empty row always at the end,
            // change this depending on your needs
            int lastRow = model.getRowCount() - 1;
            if (row == lastRow) {
                return emptyValue;
            }
            return modelWrapperImplementation.getValueAt(row, column);
        }

        //@Override
        //public String getStringValueAt(int row, int column) {
        //    //  also override this if there is no comparator definied for a column
        //}

        @Override
        public Object getIdentifier(int row) {
            return modelWrapperImplementation.getIdentifier(row);
        }

    }

     /**
      * This is a wrapper around another comparator.
      * We handle our empty value and if none, we invoke the base comparator.
      */
    private class EmptyValueComparator implements Comparator {

        private final Comparator defaultComparator;

        private final TableRowSorter tableRowSorter;

        private final int columnIndex;

        public EmptyValueComparator(Comparator defaultComparator, TableRowSorter tableRowSorter, int columnIndex) {
            this.defaultComparator = defaultComparator;
            this.tableRowSorter = tableRowSorter;
            this.columnIndex = columnIndex;
        }

        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof EmptyValue && o2 instanceof EmptyValue) {
                return 0;
            }
            if (o1 instanceof EmptyValue) {
                return adjustSortOrder(1);
            }
            if (o2 instanceof EmptyValue) {
                return adjustSortOrder(-1);
            }
            return defaultComparator.compare(o1, o2);
        }

        /**
         * Changes the result so that the empty row is always at the end,
         * regardless of the sort order.
         */
        private int adjustSortOrder(int result) {
            List sortKeys = tableRowSorter.getSortKeys();
            for (Object sortKeyObject : sortKeys) {
                SortKey sortKey = (SortKey) sortKeyObject;
                if (sortKey.getColumn() == columnIndex) {
                    SortOrder sortOrder = sortKey.getSortOrder();
                    if (sortOrder == SortOrder.DESCENDING) {
                        result *= -1;
                    }
                    return result;
                }
            }
            return result;
        }

    }

    private static class EmptyValue {}

}

现在您可以在表格中启用排序。

JTable table = ...;
TableRowSorter tableRowSorter = new EmptyTableRowSorter(table.getModel());
table.setRowSorter(tableRowSorter);

答案 3 :(得分:0)

我知道这是一个旧线程,但我被困在这里试图找到一种方法。如果有一种以前没有提到的新方法,请告诉我。实际上这是我关于SO的第一篇文章,但由于我花了很多时间为此寻找解决方案,我决定分享。

我解决了这个排序问题,而不是使用'null'作为我的'新条目',我创建了一个特定的对象(在我的情况下来自Object类本身,但它可能更像是'NewEntryDoNotCompareWithMePleaseLetMeBeTheLastEntryIBegYouDoNotSortMe'空的东西类)。

比较时,我首先检查要比较的'对象'是否是对象。当然不是'instanceof',而是(obj.getClass()== Object.class)。 - 如果是这种情况,请检查排序顺序并返回1或-1。

随意评论您发现的任何问题,如果有人需要代码,我可以尝试创建一个小工作版本。

干杯, 丹尼尔

答案 4 :(得分:-2)

使用DefaultRowSorter的setComparator方法设置不同的比较器。